Skip to content

Latest commit

 

History

History
243 lines (156 loc) · 8.02 KB

JS面试题.md

File metadata and controls

243 lines (156 loc) · 8.02 KB

01. 闭包

  1. 闭包的概念:函数执行完成后,它内部的变量会被释放,但是如果这些变量被其他函数所引用就不会被释放,产生了闭包(个人理解)

函数执行形成一个私有的上下文,此上下文中的私有变量,和上下文外的变量互不干扰,也就是把这些变量保护起来了,我们把函数的这种保护机制称之为闭包。【闭包是一种机制】

  1. 闭包的优点:
  • 可以访问其他函数内部的变量
  • 可以保存数据
  • 可以封装私有数据和方法
  1. 闭包的缺点:过度的使用闭包可能会导致占用过多的内存,在 IE 浏览器中可能会导致内存泄露

  2. 闭包产生的场景:

  • 立即执行函数(IIFE)
  • 返回一个函数
  • 函数作为另一个函数的参数
  • 定时器、事件绑定、AJAX 等的回调
  1. 闭包的应用:
  • 早期的模块化(立即执行函数)
  • 防抖节流(返回一个函数)
  • 科里化函数(返回一个函数)
  • 组合函数(返回一个函数)
  • 循环事件绑定(事件委托)

02. let 和 var 的区别

  • let 是块作用域
  • let 不会变量提升
  • let 不能重复声明
  • let 有暂时性死区(在定义之前不能访问)

补充:const 定义的常量,定义时必须赋值,且该常量的内存地址是不会改变的,但是它的值是可能改变的(比如对象,数组)

03. this 指向的几种情况

  • 自执行函数 -> window(非严格模式)
  • 普通函数执行 -> window
  • .执行 -> . 前面的内容(非严格模式)
  • 箭头函数 -> 函数定义时的上下文
  • call / apply / bind -> 第一个参数(一旦绑定,this 指向不能被改变)
  • 事件绑定 -> 绑定事件的元素(IE 6 ~ 8 attachEvent 绑定的事件指向 window)

04. 作用域

作用域分为:全局作用域、函数作用域以及 ES6 提出的块级作用域。

作用域的作用:

  • 收集并维护所有声明的标识符(变量和函数)
  • 按照指定的规则来查找标识符
  • 确定当前代码对标识符的访问权限

05. 作用域链

多个作用域之间嵌套就形成了作用域链。

词法作用域在查找标识符的时候,会先在本作用域查找,没有找到会向上一级作用域查找,一直到找到或者到全局作用域为止

06. 原型

所有的函数都有一个特殊的属性:原型(prototype)。它是一个指针,指向原型对象,原型对象上的所有属性和方法都可以被所有的实例共享。

07. 原型链

成员访问首先会找自己的私有属性,如果没有找到,会向所属类的 prototype 查找,直到被找到或者到 Object.prototype 为止,我们把这种查找机制称为“原型链”

08. 作用域链和原型链的区别

作用域链是指变量访问(查找标识符),原型链是指成员访问

09. 作用域和执行上下文的区别

作用域是在函数定义的时候就已经确定了的,函数内的变量是和函数所处的作用域有关 执行上下文就是 this,是在函数执行的时候确定的,函数每次执行都会产生一个新的执行上下文环境,比如函数每次执行的参数可能不一样

10. 继承的几种方式

  • 原型链继承:在构造函数的原型上扩展属性和方法
  • 原型式继承:使用 Object.create()
  • 拷贝继承
  • 构造函数继承:只能继承父类的私有属性和方法,无法继承父类原型的属性和方法
  • 组合继承:两次调用了构造函数,且子类实例的原型链不干净,包含了父类的私有属性和方法
  • 寄生组合继承

扩展:ES6 的 class 继承是使用的哪种继承方式?

寄生组合继承

11. 为什么要有事件循环?

因为 JS 是单线程的,如果某些任务特别耗时,没有事件循环,那么其他的任务就一直被堵塞。

12. 为什么要有微任务?只有宏任务不行吗?

宏任务的执行顺序总是和加入宏任务队列的顺序相关,如果此时我们有优先级较高的任务需要执行,只有宏任务是不够的,所以需要引入微任务,提高任务的优先级

13. 宏任务和微任务

  1. 浏览器

宏任务:

  • setTimeout
  • setInterVal
  • requestFrameAnimation
  • I/O

微任务:

  • Promise
  • MutationObserver

宏任务和微任务的执行顺序:

在一个 script 标签里,代码是自上向下执行的,如果遇到微任务会把这个微任务加入微任务队列,如果遇到宏任务会把宏任务加入宏任务队列。当同步任务执行完成后,会先清理微任务队列,如果微任务里又有微任务,宏任务,也会被加入到微任务,宏任务队列。当所有的微任务执行完之后,再去清理宏任务队列

  1. node

宏任务:

  • setImmediate
  • setTimeout
  • setInterval
  • I/O

微任务:

  • Promise
  • process.nextTick

宏任务的执行顺序:

  • timers 定时器的回调
  • pending callbacks 待定回调(比如 I/O 回调)(不包括 timers,setImmediate,close 的 callbacks)
  • idle,prepare(node 内部的)
  • poll 轮询(node 内部的)
  • check 检测(执行 setImmediate 的回调)
  • close callbacks 关闭的回调函数

宏任务和微任务的执行顺序:

  1. Node V10 及以前
  • 执行完一个阶段的所有任务
  • 执行完 nextTick 队列里面的所有内容
  • 然后执行完其他微任务队列的内容

nextTick 优先级比微任务还高,只要一个阶段的任务执行完之后,就会去先清理完 nextTick 队列,再去清理其他的微任务队列

  1. Node V11 及以后

和浏览器保持一致

setTimeout(() => {
  console.log('setTimeout')
}, 1)
setImmediate(() => {
  console.log('setImmediate')
})

上面代码输出的顺序不确定,为什么呢?

到清空 timers 回调的时候,如果主栈执行耗时小于 1 毫秒,时间还没到,就会跳过了。

14. JS 单线程 VS 浏览器多线程

JS 是单线程的,但是浏览器的多线程的,打开一个网页,浏览器会开启不同的线程执行不同的任务

浏览器的线程:

  • GUI 渲染线程
  • JS 引擎线程
  • HTTP 网络请求线程
  • 定时器监听线程
  • DOM 事件监听线程

15. Promise 和 async/await 的区别

  1. Promise 是基于约定管理异步编程,async/await 是基于 Generator 管理异步编程
  2. Promise 可以通过 then 的第二个参数 和 catch 捕获异常,async/await 通过 try catch 捕获异常

16. 遍历对象的方法有哪些

  • for in
  • Object.keys()
  • Object.values()
  • Object.getOwnPropertyNames() 可以遍历不可枚举的属性,但是不能遍历 Symbol 类型的属性
  • Reflect.ownKeys()

注意:for of 不能遍历对象

17. js 对象和 map 有什么区别?

  • 普通对象的 key 只能是字符串和 Symbol,map 的 key 可以是任意类型
  • 普通对象的遍历需使用 Object.keys() Object.values() Object.entries() 转成数组在遍历,虽然 for ... in 可以遍历,但是有些其他的限制,map 可以直接使用 for of 和 forEach 来遍历

18. Ajax、fetch、Axios 三者有什么不同?

三者都用于网络请求,但是不同维度

  • Ajax(Asynchronous Javascript and XML),一种技术统称
  • Fetch 一个具体的 API
  • Axios 第三方库

Fetch:

  • 浏览器的原生 API,用于网络请求
  • 和 XMLHttpRequest 一个级别
  • Fetch 语法更加简洁、易用,支持 Promise

Axios:

  • 最常用的网络请求库
  • 内部可用 XMLHttpRequest 和 Fetch 来实现

19. for in 和 for of 有什么区别?

  • for in 遍历可枚举的数据,比如对象、数组、字符串等,得到的是 key
  • for of 遍历拥有 Symbol.iterator 属性的数据结构(值),比如数组、字符串、Map、Set 等,得到的 value

20. for await of 有什么作用?

遍历 Promise 数组

21. JS 严格模式有什么特点?

  • 全局变量必须先声明再访问
  • 禁止使用 with
  • 创建 eval 作用域(eval 有自己单独的作用域)
  • 禁止 this 指向 window
  • 函数参数不能重名

22. for 和 forEach 遍历一个数组,哪个更快?

  • for 更快
  • forEach 每次都要创建一个函数来调用,函数需要独立的作用域,会有额外的开销