js

3/21/2022 js

# 1. call、apply、bind的区别是什么?

查看答案
  • 这三个都是改变this指针的指向
  • call和apply直接进行相关函数的调用,bind不会调用函数返回的是一个新函数
  • apply第二个参数传入的是数组或者伪数组,call和bind传入的是依次排列的参数
  • call 比 apply 的性能要好,平常可以多用 call

# 2. 判断数组方法有几种?区别是什么?

查看答案
  • Object.prototype.toString.call()
  • instanceof
  • Array.isArray()
Object.prototype.toString.call()

万能方法

Object.prototype.toString.call([1,2,3]) // '[object Array]'
1
instanceof

使用 instanceof 判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的原型,找到返回 true,否则返回 false。

[] instanceof Array; // true
[] instanceof Object; // true
1
2
Array.isArray()
  • 用来判断对象是否为数组
  • 当检测 Array 实例时,Array.isArray 优于 instanceof ,因为 Array.isArray 可以 检测出 iframes
var iframe = document.createElement('iframe')
document.body.appendChild(iframe)
xArray = window.frames[window.frames.length-1].Array
var arr = new xArray(1,2,3) // [1,2,3]
// Correctly checking for Array
Array.isArray(arr); // true
Object.prototype.toString.call(arr) // true 
// Considered harmful, because doesn't work though iframes
arr instanceof Array // false
1
2
3
4
5
6
7
8
9
  • Array.isArray()是 ES5 新增的方法,有兼容问题,当不存在 Array.isArray() ,可以用 Object.prototype.toString.call() 实现。
if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]'
  }
}
1
2
3
4
5

# 3. a.b.c.da['b']['c']['d'],哪个性能更高?

查看答案

a.b.c.d性能更高,原因是[]中会有表达式和变量的情况,编译器解析起来比点的复杂一些。

# 4. 为什么for循环的性能远远高于forEach?

查看答案
  • for循环没有任何额外的 函数调用栈上下文
  • forEach不是普通的for循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来

# 5. defer和async的区别

查看答案
  • <script>标签用于加载脚本与执行脚本,是前端开发中非常重要的标签。
  • 直接使用script脚本的话,html会按照顺序来加载并执行脚本,在脚本加载及执行过程中,会阻塞后续的DOM渲染。
  • script提供了async和defer两个属性来 解决DOM渲染阻塞的问题
  1. 没有 defer 或 async
  • <script src="script.js"></script>
  • 浏览器会立即加载并执行指定的脚本,不等待后续载入的文档元素。
  1. 有 async
  • <script async src="script.js"></script>
  • 加载和渲染后续文档元素script.js 的加载与执行 并行(异步)。
  • js执行时间:解析完成之后立即执行。
  • 多个async并行执行无法保证顺序。
  • 使用场景:
    • 脚本并不关心页面中的DOM元素(文档是否解析完毕)
    • 不会产生其他脚本需要的数据
    • 例如:百度统计、Google Analytics
  1. 有 defer
  • <script defer src="script.js"></script>
  • 加载和渲染后续文档元素script.js 的加载与执行 并行(异步)。
  • js执行时间:所有元素解析完成之后,DOMContentLoaded 事件触发之前。
  • 比async要稳定,多个defer并行执行保证加载顺序。
  • 使用场景:
    • 代码依赖于页面中的DOM元素(文档是否解析完毕)
    • 被其他脚本文件依赖
    • 例如:评论框、代码语法高亮 、polyfill.js

# 6. this的指向规则

查看答案
  1. 简单调用函数,严格模式下为undefined,非严格模式下为window
  2. New方法构造函数时,指向新创建的对象上,如果返回的是一个对象,则指向返回的对象
  3. 使用call/apply/bind时,指向第一个参数指定的对象
  4. 通过上下文调用函数时,指向对应上下文对象上
  5. 箭头函数时,对应箭头函数外的作用域
  6. 如果上下文调用中有嵌套关系,this会指向最后调用它的对象

# 7. this的优先级

查看答案
  • 显式绑定:call、apply、bind、new
  • 隐式绑定:根据调用关系确定this指向的情况
  • 优先级:
    • 显式绑定 > 隐式绑定
    • new > call、apply、bind
    • 箭头函数的this不可变

# 8. new操作符的作用

查看答案
  1. 定义一个对象
  2. 对象的__proto__属性指向new构造函数的原型对象
  3. 对象上定义一些属性和方法
  4. 返回这个对象

# 9. 作用域

查看答案
  • ES5:全局作用域、函数作用域
  • ES6:原有基础上添加了块级作用域 变量作用域查找是一句作用域链,依次跳到上层,终点是全局。

# 10. 代码执行的两个阶段?

查看答案

代码执行分为 代码预编译阶段代码执行阶段

  • 代码预编译阶段:确定作用域,进行变量声明
  • 代码执行阶段:创建执行上下文,执行上下文形成作用域链

# 11. 什么是执行上下文?

查看答案

执行上下文:当前代码的执行环境/作用域。

执行上下文包括:

  • 变量对象
  • 作用域链
  • this

# 12. 什么是函数调用栈?

查看答案

执行时入栈,可访问对应变量(预编译创建,执行激活),执行完毕后,上下文销毁,出栈

# 13. 闭包

查看答案

函数嵌套函数时,内层引用外层变量,且内层函数全局可访问,就会形成闭包。

闭包的基本原理:
外界可以通过返回的函数获取原函数内部的变量值。

闭包使用不当会造成内存泄露问题。

# 14. 加法运算时的隐式类型转换

查看答案
  • string + any => string
  • number + undefined => NaN
  • number + number、boolean、null => number
  • NaN + number、boolean、NaN、undefined、null => NaN
  • Infinity + Infinity、number、boolean、null => Infinity
  • -Infinity + (-Infinity)、number、boolean、null => -Infinity
  • Infinity + (-Infinity) => NaN
  • 引用类型:先调用valueOf或toString由规范所决定,先转成基本数据类型再字符串拼接
    • 可以通过对对象的valueOf和toString方法重写,改变 console.log(1+foo) 的结果,隐式转换更倾向于调用valueOf

优先级:string > undefined(NaN) > number > boolean

# 15. js函数参数传递

查看答案
  • 参数为基本数据类型时,复制参数值,修改不会改变原值
  • 参数为引用类型时
    • 修改某值,原值跟着修改(因为引用了同一地址)
    • 修改引用地址,修改值原值不会修改(地址改变)

# 16. Babal的核心原理

查看答案

使用AST(抽象语法树)对源码进行分析,并转为目标代码。

Babel有两种模式:

  1. normal:更接近ES6
  2. loose:纯ES5方法
更新时间: 2024-06-13 14:33