25道JS面试题总结,让你实力更上一层楼

4,380 阅读6分钟

本文正在参加「金石计划」

前言

虽然说2023年我们预想的金三变成了铜三,但是作为一个练习时长两年半的资深前端练习生,也不得不为即将到来的银四(可能是铁四)准备着。 下面我就来给大家分享 25 道 JS 高频面试题吧。助力广大志同道合的练习生们更上一层楼。

1. JS数组的常用的方法有哪些?

面对这样的问题,首先我们要自己有一套总结思路,根据数组的特点来总结,例如:增删改查、排序、转换、迭代。 这样我们才可以保证我们可以完全的给面试官总结清楚。

1.增删改查

增: push() unshift() splice() concat()
删: pop() shift() splice() slice(不改变原数组)
改: splice()
查: indexOf() includes() find()

2. 排序

sort()  reverse()

3. 转换

join()

4. 迭代

forEach()
map()
filter()
some()
every()

2. JS字符串的常用方法有哪些?

既然都问到了数组,那字符串也必不可少,我们同样采取一样的套路来总结

1.增删改查

增: concat()
删: slice() substr() substring()
改: trim() trimLeft() trimRight() trimStart() trimEnd()
repeat()  padStart() padEnd()
toLocaleLowerCase() toLocaleUpperCase()

2.转换

split()

3.模板匹配

replace()  search() match()

3. 来谈谈JS中的类型转换

面对这类问题,我们可以根据是什么、有什么、怎么用三个方面去总结,齐总这三个方面中,我们最要解释清楚的就是是什么,其他两个方面我们可以想到什么说什么。

  1. 是什么

JS的类型转换用一句话解释就是不同的类型之间运算需要先对数据的类型进行转换,类型转换是将一种数据类型转换为另一种数据类型的过程。

  1. 显示转换
  • Number()
  • String()
  • Boolean()
  • parseInt()
  1. 隐式转换
  • 比较运算符(==, !=, >, <, if, while)
  • 算数运算符(+, -, *, /, %)
  1. 引用类型转为原始类型的过程

调用对象上的toPrimitive()方法

  1. 如果obj是基本类型,直接返回
  2. 调用valueOf()方法,如果得到一个原始类型,则返回
  3. 调用toString 方法,如果得到一个原始类型,则返回
  4. 不能转换,报错

4. == VS ===

  1. 是什么
  • 对于 == ,如果双方的类型不一样的话,就会进行类型转换 再进行比较。
  • 对于 === 首先要求比较双方类型相同, 再比较值是否相同。

5. 深拷贝和浅拷贝有什么区别?实现一个深拷贝

  1. 是什么
  • 首先我们知道,基本类型存在栈中,引用类型存在堆中但是引用地址存在栈当中
  • 浅拷贝如果拷贝的是原始类型,那就是拷贝的值,如果是引用类型,则拷贝的是引用地址
  • 深拷贝通常是引用类型的拷贝,深拷贝会开辟一个新的内存空间,新的内存空间中存放的属性和值与原对象完全相同,但是引用地址不同。
  1. 深拷贝的实现
function deepClone(obj) {
  if (obj === null) return obj
  if (obj instanceof Date) return new Date(obj) 
  if (obj instanceof RegExp) return new RegExp(obj) 
  if (typeof obj === 'function') return obj
  let newObj = {}
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
    }
  }
  return newObj
}

6. 说说你对闭包的理解?

  1. 是什么

闭包就是能够读取其他函数内部变量的函数。因为在JS中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成 " 定义在一个函数内部的函数" 。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

  1. 使用场景
  • 模拟私有方法
  • setTimeout循环
  • 匿名自执行函数
  • 结果要缓存场景
  • 实现类和继承

7. 聊聊JS的事件流和事件模型

  1. 是什么

事件流描述的是从页面中接受事件的顺序。

  1. 过程
  • 事件流顺序:捕获阶段 -》 目标阶段 -》 冒泡阶段
  1. 模型
  • 原始事件模型: DOM0 级别的事件
    • 优点: 兼容性好,绑定速度快
    • 缺点: 只支持冒泡,不支持捕获
  • 标准事件模型 addEventListener
  • IE事件模型 attachEvent
    • 目标阶段 -》冒泡阶段

8. 说说你对JS中作用域的理解

  1. 是什么

作用域就是变量和函数的可访问的范围,变量和函数生效的一个区域就可以称为作用域,它控制着变量和函数的可见性和周期性。

  1. 有什么
  • 全局作用域
  • 块级做用户
  • 函数作用域
  • 词法作用域

9. 聊聊你对原型、原型链的理解

  1. 是什么
  • 所有引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象
  • 所有函数都有一个prototype(原型)属性,属性值是一个普通的对象
  • 所有引用类型的__proto__属性指向构造函数的prototype
  • 原型都是对象,且原型中存在constructor用于记录谁创建的这个原型
  1. 原型链

当我们试图去访问一个对象的属性时,浏览器引擎会在该对象上查找该属性,当我们在该对象原型没有找得到的话,我们就会顺着constructor一层一层的往上查找。我们将顺着向上找的这种行为称为原型链。

10. 聊聊JS的继承

  1. 是什么 简单来说,通过某种手段,让子类能够访问到父类的属性和方法,这种手段就叫继承

  2. 有什么

  • 原型链继承
  • 构造函数原型继承
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承

11. 说说你对js中this的理解

  1. 是什么

this关键字是函数运行时自动生成的一个内部对象, 只能在函数内使用,总是指向调用它的对象,this一旦被确定是不可以修改的。(箭头函数本身没有this,它指向的是函数外的this)

  1. 绑定规则
  • 默认绑定
    • 函数在那个词法环境生效,函数中的this就绑定在哪里
  • 隐式绑定
    • 当函数引用有上下文对象时,隐式绑定的规则就会把函数调用中的this绑定到这个上下文对象
  • 隐式丢失
    • 当隐式绑定的函数丢失了绑定对象,就会应用默认绑定(this永远指向之后调用它的那个对象)。
  • 显示绑定
    • call() apply() bind()

12. js中的执行上下文和执行栈是什么?

  1. 是什么

执行上下文 就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念,JavaScript 中运行任何的代码都是在执行上下文中运行。

执行栈也叫调用栈,是用于存储代码执行期间创建的所有执行上下文。

  1. 有什么
  • 全局执行上下文
  • 函数执行上下文
  • Eval执行上下文

13. 说说typeof 和 instanceof 的区别?

  1. 是什么

typeof 是一个操作符,主要的目的是检测一个变量是不是基本数据类型的变量,同时也可以说是确定一个变量是字符串、数值。布尔值、undefined、还是函数的一种方法。

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的上。instanceof 可以准确判断引用数据类型,但是不能正确判断原始数据类型

14. 聊聊事件委托以及应用场景

  1. 是什么

事件代理就是把原本需要绑定在子元素的响应事件(click、keydown......)委托给父元素,让父元素担当事件监听的职务。也可以理解成将多个节点要绑定的事件委托到另一个节点身上,借助了该节点的事件流机制完成我们需要的操作。

  1. 应用场景
  • 操作列表

15. 说说new都干了什么

  1. 创建一个对象
  2. 将构造函数的this指向这个对象
  3. 将对象的原型指向构造函数的原型
  4. 返回该对象

16. Ajax的原理和实现

  1. 是什么

Ajax的原理简单来说,实际上就是通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。

  1. 具体实现
function  ajax(options) {
    let {
        url,
         method= 'GET',
          data= null,
          dataType= 'JSON',
          async = true,
          cache = true,
          success,
          error
        } = options
    let xhr = new XMLHttpRequest()
    // 处理参数
    if(method.toUpperCase() === 'GET') {
        if(data) {
            url += '?'
            for(let key in data) {
                url += `${key}=${data[key]}&`
            }
            xhr.open(method, url, async)
            xhr.send()
        }
    }else {  //post
        xhr.open(method, url, async)
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        xhr.send(data)
    }
    xhr.onreadystatechange = () =>{
        if(xhr.readyState === 4){
            if( !/^(2|3)\d{2}$/.test(xhr.status)) {
                error && error(xhr.statusText, xhr)
            }
           let result =  handleDataType(xhr)  //格式化处理
           success && success(result, xhr)
        }
    }
    function handleDataType(xhr) {
       dataType = dataType.toUpperCase()
       let  result = xhr.responseText
        switch(dataType) {
            case 'TEXT':
                break;
            case 'JSON':
                result = JSON.parse(result)
                break;
            case 'XML':
                result = xhr.responseXML
                break;
            // ...
            default:
        }
        return result
    }
}

17. call, apply, bind的区别? 实现一个bind

  1. 是什么

js中的apply、call、bind是三个函数方法,都可以用来改变函数的this指向。

apply和call的作用是一样的,都是改变函数的this指向,并且立即执行该函数。区别在于传入参数的方式不同,apply接收一个数组作为参数,而call则是一个一个参数传入。

bind则是返回一个新的函数,改变函数的this指向,但不会立即执行该函数,需要手动调用。

这三个方法在实际开发中经常用到,特别是在处理函数作为参数传递的情况下,可以方便地改变函数的执行环境。

  1. bind的实现
Function.prototype.my_bind = function(obj) {
  if (typeof this !== 'function') {
    throw new TypeError('error')
  }
  obj = obj || window
  const args = Array.prototype.slice.call(arguments, 1)
  const _this = this
  const pro = function() {}
  if (this.prototype) {
    pro.prototype = this.prototype
  }
  const bound = function () {
    // 判断该函数是否被new
    return _this.apply( //
      this instanceof pro ? this : obj,
      args.concat(Array.prototype.slice.call(arguments))
    )
  }
  bound.prototype = new pro() // 继承到了foo的原型
  return bound
}

18. 说说你对Event loop的理解

  1. 是什么

Event loop 是为了解决JS单线程运行阻塞的问题,JS设计的一套循环机制。

Event Loop事件循环,其实就是JS引擎管理事件执行的一个流程,具体由运行环境确定。

  1. 有什么
  • 宏任务

    • cript, setTimeout, setInterval, I/O, UI-rendering/UI事件, postMessage
  • 微任务

    • Promise.then, nextTick, MutaionObserver
  1. 整体流程
  • 宏任务中的同步 -》
  • 微任务 -》
  • 页面渲染 -》
  • 下一次宏任务 -》

19. 说说你对DOM的理解,常见的操作有哪些?

  1. 是什么

DOM是一个对象,是HTML和XML文档的编程接口,其提供了一个结构化的表述并且定义了一种方式——程序可以对结构树进行访问,以改变文档的结构,样式和内容。 从本质上说,它将web页面和脚本或编程语言连接起来了。

  1. 常见操作
  • 创建节点

    • createElement createTextNode createDocumentFragment createAttributr
  • 查询节点

    • querSelector
  • 更新节点

    • innerHTML
  • 添加节点

    • appendChild
  • 删除节点

    • removeChild

20. 说说你对BOM的理解, 常见的BOM操作有哪些?

  1. 是什么

BOM(Browser Object Model)提供了独立于内容而与浏览器窗口进行交互的对象,主要用于管理窗口与窗口之间的通讯,因此核心对象是window。

  1. 有什么
  • history.go(-1)
  • screen
  • bavigator
  • location
  • window.open

21. 说说JS内存泄露的几种情况

  1. 是什么

由于某些原因,导致程序在执行过程中开销的内存在程序执行完毕后没有被释放,造成了内存浪费

  1. 情况
  • 闭包
  • 回调地域
  • 递归
  • 浏览器的垃圾回收机制

22. JS中本地存储的方式有哪些,区别和应用场景呢?

  1. 有什么

下面我从 name、 size、 when、 where四个方面介绍:

  • sessionStorage 5~10M 会话期间 本地保存
  • loaclStorage 5~10M 永久 本地保存
  • cookies 4kb 过期时间之内一直有效 自动相互传递
  • indexedDB 无限大 永久 本地保存
  1. 区别

空间大小、生命周期、数据与服务器之间的交互方式各有不同。

  1. 应用场景
  • sessionStorage: 敏感账号的一次性登录
  • loaclStorage: 缓存不敏感的数据
  • cookies: 跟踪用户行为
  • indexedDB: 存放大文件

23. 说说函数式编程的优缺点和理解

  1. 是什么

函数式编程是一种编程范式,它将电脑运算视为函数运算,并且避免使用程序状态以及易变对象 其中,λ演算是该语言最重要的基础。而且λ演算的函数可以接受函数作为输入的参数和输出的返回值。

  1. 优缺点
  • 更好管理状态,减少出错
  • 复用性高
  • 代码的可维护性高
  • 性能降低,上下文切换开销变大
  • 浏览器垃圾回收困难

24. 说说JS数字精度丢失问题,怎么解决?

  1. 是什么

由于计算机不能存储无限长度的小数,会取其近似值。而JS的Number采取的是IEEE754规范的64位双精度浮点数 (意味着浮点数二进制X = (-1)^s * M * 2^e)。

  1. 解决
  • Math.js
  • bigNumber.js
  • (x * 10^a) / 10^a

25. 说说函数的防抖和节流、怎么实现?

  1. 是什么

函数的防抖和节流都是用来优化体验,减少相同函数没有必要的调用

防抖是在规定时间内,没有再次触发,则执行函数 节流是在规定时间内,不管函数触发多少次,只执行一次

  1. 实现
  • 防抖
function debounce(fn, wait) {
    var timeout;
    return function() {  
        const context = this
        const args = arguments
        clearTimeout(timeout)
        timeout = setTimeout(function() {
            fn.apply(context, args)
        }, wait);
    }
}
  • 节流
function throttle(fn, wait) {
    var pre = 0
    return function() {
        var now = +new Date()
        const context = this
        if(now - pre > wait){
            fn.call(context, e)
            pre = now
        }
    }
}

总结

虽然上述的25道JS面试题的难度不会很高,但是被面试官提问的概率还是很高的(亲身经历),希望各位练习生们 取其精华,去其糟粕,提取自己想要的内容,同时也欢迎你们提出宝贵的意见。