js面试题集合

根据近几年在前端中厂、大厂面试的同学的经历,总结出我们在拿offer前经常被问到的问题,有关JavaScript的问题能拿的出手的不是很多也不会很复杂,还是得需要我们对这门语言精通。

面试官不会精确到问你一个小问题,主要还是让我们谈一谈,说一说,发挥我们的语言组织能力,谈到这个知识点的时候你要是不会回答,就按是什么?有什么作用特点?再进行扩展开来聊。让面试官跟着我们的引导来走。扩展开来还是得按我们会的扩展。怕说了什么重要的自己答不上来,尽量往自己会的方向走。

接下来我们就看一看JavaScript的考点,当问到这些再慢慢学着扩展到下个问题。

1.js数组上面有哪些方法?

1. 增:

  • push:将一个或多个元素添加到数组的末尾,并返回新数组的长度
  • unshift:将一个或多个元素添加到数组的开头,并返回新数组的长度。
  • splice:可以删除现有元素、添加新元素或替换数组中的元素。它接受多个参数,分别表示起始位置、要删除的元素个数(如果是 0 则表示不删除任何元素)、要添加的元素(可选)。 添加元素的位置由起始确定。以数组的形式返回删除的元素。
js 复制代码
let arr = [1,2,3,4,5]
let len = arr.splice(1,2,6)
console.log(len);  //[2,3]
  • concat:用于合并两个或多个数组,并返回一个新数组,而不改变现有数组。

2.删:

  • pop:从数组中移除最后一个元素,并返回该元素的值。同时,原数组的长度减一。
  • shift:从数组中移除第一个元素,并返回该元素的值。同时,原数组的长度减一,并且所有后续元素的索引减一。
  • splice
  • slice:返回一个新数组,其中包含从开始到结束(不包括结束)的选定元素。原始数组不会被修改。一个参数默认到数组结尾。
js 复制代码
let fruits = ['apple', 'banana', 'cherry', 'orange', 'pear'];
let selectedFruits = fruits.slice(1, 4);
// selectedFruits 现在为 ['banana', 'cherry', 'orange'],fruits 不变

3.改:

  • reverse:用于颠倒数组中元素的顺序,即将数组中的元素倒序排列。
  • sort:用于对数组中的元素进行排序。

4.查:

  • indexOf:查询数组中的元素是否存在,存在就返回数组中第一个匹配项的索引值,如果不存在则返回 -1。
  • lastIndexOf:返回数组中最后一个匹配项的索引,如果不存在则返回 -1。
  • filter:创建一个新数组,其中包含通过指定函数测试的所有元素。
js 复制代码
let numbers = [1, 2, 3, 4, 5];
let evenNumbers = numbers.filter(num => num % 2 === 0);
// evenNumbers 现在为 [2, 4],包含了原数组中所有偶数
  • find:返回数组中满足提供的测试函数的第一个元素的值。如果没有找到,则返回 undefined
js 复制代码
let numbers = [1, 2, 3, 4, 5];
let firstEvenNumber = numbers.find(num => num % 2 === 0);
// firstEvenNumber 的值为 2,因为它是数组中第一个偶数
  • includes:判断数组是否包含某个元素,如果包含则返回 true,否则返回 false

5.转换:

  • join:用于将数组中所有元素连接成一个字符串。你可以使用指定的分隔符来连接数组元素,如果不提供分隔符,默认使用逗号作为分隔符。

6.迭代:

  • forEach:对数组中的每个元素执行指定的函数一次。它没有返回值(或者说返回值为 undefined)。
js 复制代码
let numbers = [1, 2, 3, 4, 5];
numbers.forEach(num => {
    console.log(num);
});
// 输出:
// 1
// 2
// 3
// 4
// 5
  • map:创建一个新数组,其元素是对原始数组中每个元素调用提供的函数的结果。
js 复制代码
let numbers = [1, 2, 3, 4, 5];
let doubledNumbers = numbers.map(num => num * 2);
// doubledNumbers 现在为 [2, 4, 6, 8, 10]
  • reduce:对数组中的每个元素执行一个提供的 reducer 函数(从左到右),将其结果汇总成单个值。
js 复制代码
let numbers = [1, 2, 3, 4, 5];
let sum = numbers.reduce((acc, cur) => acc + cur, 0);
// sum 的值为 15,即 1 + 2 + 3 + 4 + 5
//acc是累加器,设置初始值为0,函数再执行一遍acc的值是上次函数执行的结果。
  • filter:创建一个新数组,其中包含通过指定函数条件的所有元素。
  • set:是 ES6 中引入的一种新的数据结构,它类似于数组,但是成员的值都是唯一的。

7.Array.from()

用来创建新数组的静态方法。它可以将类数组对象或可迭代对象(比如 Set、Map、字符串等)转换成一个新的数组实例。

Array.from() 方法的基本语法如下:

javascriptCopy 复制代码
Array.from(arrayLike,[mapFunction,[thisArg]])
  • arrayLike:要转换为数组的类数组对象或可迭代对象。
  • mapFunction (可选):对每个元素进行处理的映射函数。
  • thisArg (可选):执行 mapFunction 时使用的 this 值。

下面是一些 Array.from() 方法的示例用法:

  1. 将类数组对象转换为数组:
javascriptCopy 复制代码
let arr = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
let new = Array.from(arr);
console.log(new); // 输出: ['a', 'b', 'c']
  1. 使用映射函数处理每个元素:
javascriptCopy 复制代码
let set = new Set([1, 2, 3]);
let newArray = Array.from(set, x => x * 2);
console.log(newArray); // 输出: [2, 4, 6]
  1. 使用箭头函数改变 this 值:
javascriptCopy 复制代码
let arrayLike = 'hello';
let newArray = Array.from(arrayLike, function(char) {
    return this.toUpperCase();
}, 'world');
console.log(newArray); // 输出: ['W', 'O', 'R', 'L', 'D']

2.js字符串上有哪些方法?

1.增:

concat:用于连接两个或多个字符串,并返回一个新的字符串,而不会改变原始字符串。

js 复制代码
const str1 = 'Hello';
const str2 = 'world';
const str3 = '!';
const result = str1.concat( ' ' ,str2, str3);//添加一个空格

2.删:

  1. slice:接收两个参数 [包括起始,终止可选),返回删除的字符,不修改原字符串。

    如果接受的参数为负数则从字符串末尾从1的索引开始或终止,否则是空。

js 复制代码
let str = 'hello,world'  
  
let s1 = str.slice(0,2) //he
let s1 = str.slice(0)  //hello,world

let s1 = str.slice(-5,-1)  //worl
let s1 = str.slice(-2,0)   //空
  1. substring:接收两个参数,[起始,可选的结束),只接收正数,返回删除的元素,不修改原字符串。

  2. substr:被弃用,不推荐使用,和slice相像,但是第二个参数是要删除的个数。

5.改:

  1. replace :接收两个参数,被替换和替换的元素,返回一个新的字符串,不修改原字符串。可以使用正则匹配。

  2. trim :可以去除字符串两端的空格,换行符,制表符。内部的无法去除。

  3. padStart:接收两个参数,第一个是整个字符串可以到达的长度,第二个是需要添加的字符串。字符串长度超过第一个参数就不会再添加。返回新字符串,不修改原字符串。

js 复制代码
let str ='hello'
console.log(str.padStart(10,'h')); // hhhhhhello
console.log(str.padStart(10));  //补充空格

let str ='hello'
let s1 = str.padStart(10,'h')
console.log(s1,str);//hhhhhhello 和 hello
  1. padEnd :和padStart相似,但是从末尾添加。

  2. trimStart :移除字符串开头的空格,返回新字符串,不修改原字符串。

  3. trimEnd:移除字符串末尾的空格,返回新字符串,不修改原字符串。

  4. toupCase:将字符串大写,无法选择需要大写的字符,返回新字符串,不修改原字符串。

  5. toLowerCase将字符串小写,无法选择需要小写的字符,返回新字符串,不修改原字符串。

6.查:

  1. indexOf:两个参数,需要匹配的字符,起始位置(可选),找到返回第一个匹配项索引,没有返回-1.

  2. lastIndexOf:两个参数,需要匹配的字符,起始位置(可选),找到返回最后一个匹配项索引,没有返回-1.

  3. includes:两个参数,需要匹配的字符,起始位置(可选),找到为true,没有返回false.

  4. startsWith:是否以某些字符串开头。两个参数,需要匹配的字符,起始位置(可选),是为true,没有返回false.

  5. endsWith:是否以某些字符串结尾。两个参数,需要匹配的字符,起始位置(可选),是为true,没有返回false.

7.转换:

  • split:将字符串分割成子串并存入数组,不影响原字符串。
js 复制代码
s="dog cat"
let arr = s.split(" ")
arr=['dog','cat']

3.谈谈js当中的类型转换机制

  • 是什么: js中有原始类型和引用类型:
    • 原始类型:number string boolean symbol null undefined Bigint
    • 引用类型:Object Function Array Date RegExp Map Set WeakMap WeakSet

通常开发过程中,会用到一些显示的类型转换的手段来完成逻辑开发 .....

在v8执行的过程当中还存在另一种类型转换 --- 隐式类型转换

通常发生在 : 比较运算符 和算数运算符

  • 比较运算符: == === != !== < <= > >= if while

  • 算术运算符: + - * / %

[]==![] ???

这个涉及到隐式转换的问题,提到这个问题的,我们需要了解一个东西,那就是由复杂数据类型转换为基本数据类型。将引用类型转换为原始类型,在拆箱过程中会遵循ECMAScript规定的toPrimitive原则,对引用类型进行转换。在js中将复杂数据类型转换为基本数据类型会自动调用toPromitive函数。

!不会参与类型转换,但是!会希望[]变成布尔类型,所以右边的[]会变为true。左边的[]会调用原型上的toString方法转化为字符串。因为等号要使用数字判断,所以会触发Number方法转换为数字比较。

\] == !true \[\] == false '' == false 0 == 0 # 4. == 和 === 的区别? 1. == 是比较两个值,类型不一样会进行隐式转换,=== 是比较两个值的类型和值。 # 5.深浅拷贝的区别?如何实现一个深拷贝? [深浅拷贝](https://juejin.cn/post/7305321063668645888 "https://juejin.cn/post/7305321063668645888") 1. 深浅拷贝通常只针对于引用类型 * 浅拷贝: 只拷贝一层对象,复制这一层对象的原始值,如果有引用类型的话,就复制它的指针。 * 浅拷贝方法 1. object.assign() **let newObj = Object.assign({},obj)** 2. concat() **let newObj =\[\].concat(obj)** 3. 数组解构 ```js let arr = [1,2,3,{n:1}] let newObj = [...arr] arr[3].n = 2 console.log(newObj) ``` * 浅拷贝函数手写 ```js function shallowCopy(obj) { let newObj = {} for(let key in obj){ if(obj.hasOwnProperty(key)){ //显示原型上面的属性不拷贝 newObj[key] = obj[key] } } return newObj } ``` * 深拷贝: 层层拷贝,所有类型的属性值都会被复制,原对象的修改不会影响拷贝后的对象。 * 深拷贝方法 1. JSON.parse(JSON.stringify(obj)) ----无法处理undefined和symbol,function,循环引用 2. structuredClone(): 无法处理 symbol,funnction 3. [能处理循环引用](https://juejin.cn/post/7305321063668645888 "https://juejin.cn/post/7305321063668645888") * 手写深拷贝函数 ```js function deepCopy(obj){ let newObj = {} for(let key in obj){ if(obj.hasOwnProperty(key)){//用于检查对象自身是否具有指定的属性(而不是从原型链上继承而来的属性) if(typeof obj[key] !== 'object' || obj[key] === null){ newObj[key] = obj[key] }else{ newObj[key] = deepCopy(obj[key]) } } } return newObj } ``` # 6.说说你对闭包的理解? [闭包](https://juejin.cn/post/7300789760703283226 "https://juejin.cn/post/7300789760703283226") * 是什么: 当一个函数中的内部函数被拿到函数外部调用,又因为在js中内层作用域总是能访问外层作用域,那么内部函数存在对外部函数中变量的引用,这些变量的集合称之为闭包。 * 使用场景: 1. 创建私有变量 (全局变量不易维护) 2. 延长变量的生命周期 3. 实现颗粒化(柯里化) * 缺点: 会造成内存泄漏,内存被占据 # 7.什么是柯里化? * 是什么: 将一个接收多个参数的函数转变成多个只接受一个参数的函数 # 8.说说你对作用域的理解? [作用域](https://juejin.cn/post/7294474535022051355 "https://juejin.cn/post/7294474535022051355") * 是什么: 变量和函数能够生效的区域,这个区域叫作用域。 * 作用域的划分: 1. 全局作用域 2. 函数作用域 3. 块级作用域(const\|\|let 和{}) * 作用域链 作用域只能从内到外的访问,这种访问规则形成的链状关系我们称之为作用域链 * 词法作用域 指的是函数或者变量定义的区域 # 9.说说你对原型的理解 [原型](https://juejin.cn/post/7302254338856058899 "https://juejin.cn/post/7302254338856058899") * 是什么 显式原型:指的是函数身上自带的prototype属性,通常可以将一些方法和属性添加到到显示原型上,这样所有的实例对象都可以共享这些方法和属性。 隐式原型:__proto__是对象这种结构上的一个属性,其中包含了创建该对象时,隐式继承到的属性。 * 原型链: 创建一个实例对象时,实例对象的隐式原型 === 创建该对象的构造函数的显示原型,在js中对象的查找规则是先在对象中查找,找不到再去对象的隐式原型上查找,顺着隐式原型一层层往上找,直到找到null。 * 可用来实现属性的继承 # 10. 说说js中的继承 [继承](https://juejin.cn/post/7345757062108807220 "https://juejin.cn/post/7345757062108807220") * 是什么: 在js中,让一个子类可以访问父类的属性和方法 # 11. 说说js中的this [this的指向](https://juejin.cn/post/7301952490366500891 "https://juejin.cn/post/7301952490366500891") * 是什么: this是函数在运行过程中自动生成的一个对象,用来代指作用域的指向。 * 绑定规则 1. 默认绑定:当函数被独立调用时,函数的this指向window。(函数的词法作用域在哪里,this就指向哪个词法作用域,一层层往上找,直到找到最外层。) 2. 隐式绑定:当函数被一个对象所调用时,函数的this指向这个对象。 3. 隐式丢失:当函数调用前有多个对象,函数的this指向最近的对象。 4. 显式绑定:call,apply,bind。 5. new绑定:this指向实例对象。 * 箭头函数 箭头函数中的this是它外层非箭头函数的,指向也按照上述的绑定规则。 # 12.new的实现原理 1. 创建一个空对象 2. 将构造函数的原型绑定到对象的原型上 3. 将构造函数的this指向对象 4. 判断构造函数的返回值是否是引用类型,是就返回,不是就返回对象 * 构造函数有返回值,且为引用类型时会覆盖new当中的返回值 手写一个new实现 ```js Person.prototype.getName = function() { return this.name; } function Person(name) { this.name = name; return function () {} } function myNew(...args) { // [Person, 'Tom'] let obj = {} obj.__proto__ = args[0].prototype const res = args[0].apply(obj, args.slice(1)) return (typeof res === 'object' && res !== null) ? res : obj } let p = myNew(Person, 'Tom') ``` # 13. call,apply,bind 区别 [this的指向](https://juejin.cn/post/7301952490366500891 "https://juejin.cn/post/7301952490366500891") call:零散传参 call(obj,1,2) apply:数组传参 bind:零散传参,还会返回一个函数,需要调用这个函数,参数可以写在这个函数里面 # 14.说说js中的事件模型 [事件模型](https://juejin.cn/post/7319706549915107347 "https://juejin.cn/post/7319706549915107347") * 是什么 捕获,目标,冒泡三个阶段 * 分类 1. DOM0级:onclick(无法控制事件在捕获冒泡哪个阶段执行) 2. DOM1级:addeventListen(可以控制事件执行在哪个阶段) 3. IE模型:attachEvent(无法控制事件在捕获冒泡哪个阶段执行) # 15.说说typeof 和 instanceof的区别? [类型判断](https://juejin.cn/post/7304537142147416102 "https://juejin.cn/post/7304537142147416102") * typeof 能判断除了null之外的所有原始类型 * instanceof 主要适用于检查对象是否是由特定类或构造函数创建的实例。 * Object.prototype.toString.call(x) 1. \[\].toString() 数组版本的toString 2. Object.prototype.toString.call(\[\]) 3. \[\].toString() 对象版本的toString 该方法会让变量 x 调用对象上的toString函数,而toString返回值为\[object Object\]类型 * Array.isArray() 判断是否是数组 # 16. 说说Ajax的原理 [手写ajax](https://juejin.cn/post/7312059910485065791 "https://juejin.cn/post/7312059910485065791") * 是什么 Ajax * 实现过程 1. 创建一个XHR实例对象 2. 调用实例对象中的open方法与服务器建立连接 3. 调用实例对象的send方法发送请求 4. 监听onreadystatechange事件,通过判断readyState的值来获取到最终数据 5. 将数据更新到页面 # 17. 事件代理 * 事件委托 (多个子容器需要绑定相同的事件) 当我们操作大量数据时需要绑定事件,最简便的方式是循环为每一条数据绑定事件,这样会导致性能浪费。 所以需要用的事件代理。绑定父容器实现绑定一个事件代理到所有数据。 ```html

  • 1
  • 2
  • 3
  • 4
  • 5
``` # 18. 说说js当中的事件循环 [事件循环](https://juejin.cn/post/7307894647656054823 "https://juejin.cn/post/7307894647656054823") * 是什么: JS引擎在执行js过程中会区分同步和异步代码,先执行同步再执行异步,异步中同样先执行同步,再执行异步,以此往复的循环。 * 异步 1. 宏任务:script、setTimeout、setInterval、setImmediate、I/O、UI-rendering、 postMassage()、 MessageChannel 2. 微任务:.then() nextTick(node) MutationObserver回调 * Event Loop: 1. 执行同步代码 (也叫宏任务) 2. 执行微任务(完毕) 3. 有需要的话就渲染页面 4. 执行宏任务(下一次事件循环的开始) # 19.let const var区别 [作用域和变量声明](https://juejin.cn/post/7294474535022051355 "https://juejin.cn/post/7294474535022051355") * var 1. var可以重复声明变量,let和const不行 2. var声明的变量会添加到window上 3. var声明的变量会存在声明提升,提升到当前作用域的顶端,let,const不行。 * let,const 1. {}+let或const 会形成块级作用域,无法从外部访问。 2. const和let 形成的块级作用域会形成暂时性死区。 3. const 声明无法修改变量。 # 20.包装类 [包装类](https://juejin.cn/post/7301199559174062114 "https://juejin.cn/post/7301199559174062114") 原始类型不能具有属性和方法 ```js let a = 1 a.name = 123 console.log(a.name) //undefined v8开始会把a当成字符串对象,当读到定义的a后又会删除 ``` let s = 'asdfa'相当于new String('asdfa') let s = new String('asdad')可以添加属性

相关推荐
uhakadotcom2 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
范文杰2 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪2 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪3 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy3 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom4 小时前
快速开始使用 n8n
后端·面试·github
uhakadotcom4 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom4 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom4 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom4 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试