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.深浅拷贝的区别?如何实现一个深拷贝?

深浅拷贝

  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. 能处理循环引用
    • 手写深拷贝函数

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.说说你对闭包的理解?

闭包

  • 是什么:

    当一个函数中的内部函数被拿到函数外部调用,又因为在js中内层作用域总是能访问外层作用域,那么内部函数存在对外部函数中变量的引用,这些变量的集合称之为闭包。

  • 使用场景:

    1. 创建私有变量 (全局变量不易维护)
    2. 延长变量的生命周期
    3. 实现颗粒化(柯里化)
  • 缺点:

    会造成内存泄漏,内存被占据

7.什么是柯里化?

  • 是什么: 将一个接收多个参数的函数转变成多个只接受一个参数的函数

8.说说你对作用域的理解?

作用域

  • 是什么:

    变量和函数能够生效的区域,这个区域叫作用域。

  • 作用域的划分:

    1. 全局作用域
    2. 函数作用域
    3. 块级作用域(const||let 和{})
  • 作用域链 作用域只能从内到外的访问,这种访问规则形成的链状关系我们称之为作用域链

  • 词法作用域 指的是函数或者变量定义的区域

9.说说你对原型的理解

原型

  • 是什么

    显式原型:指的是函数身上自带的prototype属性,通常可以将一些方法和属性添加到到显示原型上,这样所有的实例对象都可以共享这些方法和属性。

    隐式原型:__proto__是对象这种结构上的一个属性,其中包含了创建该对象时,隐式继承到的属性。

  • 原型链: 创建一个实例对象时,实例对象的隐式原型 === 创建该对象的构造函数的显示原型,在js中对象的查找规则是先在对象中查找,找不到再去对象的隐式原型上查找,顺着隐式原型一层层往上找,直到找到null。

  • 可用来实现属性的继承

10. 说说js中的继承

继承

  • 是什么: 在js中,让一个子类可以访问父类的属性和方法

11. 说说js中的this

this的指向

  • 是什么: 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的指向 call:零散传参 call(obj,1,2)

apply:数组传参

bind:零散传参,还会返回一个函数,需要调用这个函数,参数可以写在这个函数里面

14.说说js中的事件模型

事件模型

  • 是什么 捕获,目标,冒泡三个阶段
  • 分类
  1. DOM0级:onclick(无法控制事件在捕获冒泡哪个阶段执行)
  2. DOM1级:addeventListen(可以控制事件执行在哪个阶段)
  3. IE模型:attachEvent(无法控制事件在捕获冒泡哪个阶段执行)

15.说说typeof 和 instanceof的区别?

类型判断

  • 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

  • 是什么 Ajax

  • 实现过程

    1. 创建一个XHR实例对象
    2. 调用实例对象中的open方法与服务器建立连接
    3. 调用实例对象的send方法发送请求
    4. 监听onreadystatechange事件,通过判断readyState的值来获取到最终数据
    5. 将数据更新到页面

17. 事件代理

  • 事件委托 (多个子容器需要绑定相同的事件)

当我们操作大量数据时需要绑定事件,最简便的方式是循环为每一条数据绑定事件,这样会导致性能浪费。 所以需要用的事件代理。绑定父容器实现绑定一个事件代理到所有数据。

html 复制代码
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

 <script>
        let ul = document.querySelector("ul");
        ul.addEventListener("click", (event)=>{
            console.log(event.target.innerHTML);
        }
            
        );
    </script>

18. 说说js当中的事件循环

事件循环

  • 是什么: 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区别

作用域和变量声明

  • var

    1. var可以重复声明变量,let和const不行
    2. var声明的变量会添加到window上
    3. var声明的变量会存在声明提升,提升到当前作用域的顶端,let,const不行。
  • let,const

    1. {}+let或const 会形成块级作用域,无法从外部访问。
    2. const和let 形成的块级作用域会形成暂时性死区。
    3. const 声明无法修改变量。

20.包装类

包装类

原始类型不能具有属性和方法

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')可以添加属性

相关推荐
nothing_more_than2 小时前
draggable的el-dialog实现对话框标题可以选择
javascript·vue.js·element-plus
小镇程序员3 小时前
vue2 src自定义事件
前端·javascript·vue.js
炒毛豆4 小时前
vue3+echarts+ant design vue实现进度环形图
javascript·vue.js·echarts
AlgorithmAce6 小时前
Live2D嵌入前端页面
前端
nameofworld6 小时前
前端面试笔试(六)
前端·javascript·面试·学习方法·递归回溯
前端fighter6 小时前
js基本数据新增的Symbol到底是啥呢?
前端·javascript·面试
流着口水看上帝6 小时前
JavaScript完整原型链
开发语言·javascript·原型模式
guokanglun6 小时前
JavaScript数据类型判断之Object.prototype.toString.call() 的详解
开发语言·javascript·原型模式
GISer_Jing6 小时前
从0开始分享一个React项目:React-ant-admin
前端·react.js·前端框架
川石教育7 小时前
Vue前端开发子组件向父组件传参
前端·vue.js·前端开发·vue前端开发·vue组件传参