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([1,2,3]) // '[object Array]'
1
使用 instanceof 判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的原型,找到返回 true,否则返回 false。
[] instanceof Array; // true
[] instanceof Object; // true
1
2
2
- 用来判断对象是否为数组
- 当检测 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
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
2
3
4
5
# 3. a.b.c.d
和 a['b']['c']['d']
,哪个性能更高?
查看答案
a.b.c.d
性能更高,原因是[]中会有表达式和变量的情况,编译器解析起来比点的复杂一些。
# 4. 为什么for循环的性能远远高于forEach?
查看答案
- for循环没有任何额外的 函数调用栈 和 上下文
- forEach不是普通的for循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来
# 5. defer和async的区别
查看答案
<script>
标签用于加载脚本与执行脚本,是前端开发中非常重要的标签。- 直接使用script脚本的话,html会按照顺序来加载并执行脚本,在脚本加载及执行过程中,会阻塞后续的DOM渲染。
- script提供了async和defer两个属性来 解决DOM渲染阻塞的问题。
- 没有 defer 或 async
<script src="script.js"></script>
- 浏览器会立即加载并执行指定的脚本,不等待后续载入的文档元素。
- 有 async
<script async src="script.js"></script>
- 加载和渲染后续文档元素 和 script.js 的加载与执行 并行(异步)。
- js执行时间:解析完成之后立即执行。
- 多个async并行执行无法保证顺序。
- 使用场景:
- 脚本并不关心页面中的DOM元素(文档是否解析完毕)
- 不会产生其他脚本需要的数据
- 例如:百度统计、Google Analytics
- 有 defer
<script defer src="script.js"></script>
- 加载和渲染后续文档元素 和 script.js 的加载与执行 并行(异步)。
- js执行时间:所有元素解析完成之后,DOMContentLoaded 事件触发之前。
- 比async要稳定,多个defer并行执行保证加载顺序。
- 使用场景:
- 代码依赖于页面中的DOM元素(文档是否解析完毕)
- 被其他脚本文件依赖
- 例如:评论框、代码语法高亮 、polyfill.js
# 6. this的指向规则
查看答案
- 简单调用函数,严格模式下为undefined,非严格模式下为window
- New方法构造函数时,指向新创建的对象上,如果返回的是一个对象,则指向返回的对象
- 使用call/apply/bind时,指向第一个参数指定的对象
- 通过上下文调用函数时,指向对应上下文对象上
- 箭头函数时,对应箭头函数外的作用域
- 如果上下文调用中有嵌套关系,this会指向最后调用它的对象
# 7. this的优先级
查看答案
- 显式绑定:call、apply、bind、new
- 隐式绑定:根据调用关系确定this指向的情况
- 优先级:
- 显式绑定 > 隐式绑定
- new > call、apply、bind
- 箭头函数的this不可变
# 8. new操作符的作用
查看答案
- 定义一个对象
- 对象的
__proto__
属性指向new构造函数的原型对象 - 对象上定义一些属性和方法
- 返回这个对象
# 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有两种模式:
- normal:更接近ES6
- loose:纯ES5方法