JS中的箭头函数有哪些特点?
特性 | 优点 | 缺点 |
---|---|---|
语法简洁 | 箭头函数语法更短、更简洁,适合简单的回调函数或匿名函数。 | 复杂逻辑中使用隐式返回或多层嵌套时,可能降低代码可读性和可维护性。 |
词法作用域绑定 this |
箭头函数继承定义时的 this ,避免了传统函数中 this 动态绑定的问题。 |
无法动态绑定 this ,在需要动态绑定 this 的场景(如对象方法)中不适合使用。 |
没有 arguments 对象 |
避免与普通函数中的 arguments 冲突,可以使用剩余参数(...args )代替。 |
缺乏 arguments 对象,在需要访问动态参数列表的场景下可能带来不便。 |
不能用作构造函数 | 避免误用为构造函数的风险(箭头函数不能通过 new 调用)。 |
在需要定义构造函数的场景中无法使用箭头函数。 |
隐式返回 | 单行箭头函数支持隐式返回,无需使用 return 关键字。 |
隐式返回在复杂逻辑中可能导致代码难以理解。 |
调试困难 | - | 箭头函数的简洁性可能导致代码可读性下降,调试和维护可能会变得困难。 |
不能用作生成器函数 | - | 箭头函数不能用作生成器函数(无法与 function* 结合使用)。 |
适用场景 | 回调函数、不需要动态绑定 this 的场景、简短的函数逻辑。 |
需要动态绑定 this 的场景、需要访问 arguments 对象的场景、复杂的逻辑、生成器函数场景。 |
JS中apply
, call
, bind
的作用和区别是什么?
特性 | apply |
call |
bind |
---|---|---|---|
调用方式 | 立即调用函数 | 立即调用函数 | 返回一个新函数 |
参数传递 | 参数以数组或类数组形式传递 | 参数逐个传递 | 参数逐个传递,可预填部分参数 |
适用场景 | 参数数量不确定时(如动态参数) | 参数数量固定时 | 需要延迟调用或复用绑定函数时 |
是否修改原函数 | 否 | 否 | 否(返回新函数,不修改原函数) |
JS中map
和weakMap
的区别是什么?
特性 | Map |
WeakMap |
---|---|---|
键的类型 | 可以是任何类型(包括对象、原始值) | 只能是对象或者non-registered symbols |
值的类型 | 可以是任何类型 | 可以是任何类型 |
键的引用方式 | 强引用 | 弱引用 |
垃圾回收 | 键对象不会被垃圾回收 | 如果键对象没有其他引用,则会被回收 |
可迭代性 | 支持(可通过 keys() 、values() 等方法遍历) |
不支持(无法直接遍历或获取大小) |
获取大小 | 支持(通过 size 属性) |
不支持 |
典型应用场景 | 通用键值对存储 | 私有数据存储、避免内存泄漏 |
简单描述一下JS中的事件循环机制?
简单描述一下JS中的Promise
以下代码输出什么?
1 | [1, 2, 3].map(parseInt); |
答案:[1, NaN, NaN]
解析:先看一下MDN上关于Array.prototype.map
的定义,这里我们没有传递第二个参数,所以使用的是下面第一种调用方式。
1 | map(callbackFn) |
而callbackFn
接受三个参数
- element:当前正在处理的元素
- index:当前正在处理的元素的索引
- array:调用
map
方法的数组
而parseInt
函数有如下两种形式,由于map传递过来的参数有三个,所以这里会调用第二种形式。
1 | parseInt(string) |
所以上面的代码就变成了如下形式
1 | [1, 2, 3].map((element, index, array) => parseInt(element, index)); |
展开之后相当于三次parseInt
调用
1 | parseInt(1, 0); // 1 |
注意:parseInt
函数的第二个参数是进制,这个参数有如下限制
- 如果传递的进制不在2-36之间,那么
parseInt
返回NaN
。 - 如果不传递该参数,或者传递0,那么
parseInt
会根据第一个参数推断:- 如果第一个参数以
0x
或者0X
开头,那么会被解析为十六进制。 - 否则会被解析为十进制。
- 如果第一个参数以
所以:
parseInt(1, 0);
radix为0,根据第一个参数判断,而第一个参数1并非以0x
或0X
开头,所以会被解析为十进制,返回1。parseInt(2, 1);
radix为1,不在2-36之间,返回NaN
。parseInt(3, 2);
radix为2,但是3不是二进制数,返回NaN
。
需要注意的是:parseInt
会将第一个参数转换为字符串,然后再解析。所以parseInt(3, 2);
等价于parseInt('3', 2);
。可是二进制数中不可能有3这个数字,所以返回NaN
。一个合法的例子是parseInt('11', 2);
,这个会返回3。
大数相加
实现一个函数,输入两个字符串,返回它们的和。这两个字符串表示的数字不会以0开头,且不会以空格开头。返回的结果也不会以0开头。
这个题考察的点有以下几个
- 字符串如何转换为整数?
- 进位处理
- 字符串对齐处理,如何填充前导0?
- 如何取整。
1 | function bigNumberAdd(a, b) { |
JS中如何判断Primitive类型?
可以使用Object()
函数。
1 | function isPrimitive(value) { |
JS中有哪些方法可以判断数组类型?
Array.isArray([])
[] instanceof Array
Object.prototype.toString.call([]) === '[object Array]'
Array.prototype.isPrototypeOf([])
[].__proto__ === Array.prototype
[].constructor === Array
深拷贝要注意哪些问题?
- 循环引用
- 特殊对象,比如
Date
,RegExp
,Map
,Set
,Function
等。
以下是一个简单的实现。
1 | function deepCopy(obj, hash = new WeakMap()) { |
类型转换,以下代码输出什么?
详情请参考+
的运算规则:
1 | console.log([] + []); |
输出结果是什么?
1 | const numbers = [1, 2, 3]; |
以下代码输出什么?
1 | const a = {}; |
答案:456
本题考察的点是:对象的键名只能是字符串或者Symbol类型
,如果不是,会被转换为字符串。所以a[b]
和a[c]
都会把键名转换为[object Object]
,所以a[b]
和a[c]
实际上是同一个键,最后赋值会覆盖前面的值。