当前期刊数: 261

符号 ... 在 JS 语言里同时被用作 Rest 与 Spread 两个场景,本周我们就结合 Rest vs Spread syntax in JavaScript 聊聊这两者的差异以及一些坑。

概述

Spread

... 作为 Spread 含义时,效果为扩散对象的属性:

1
2
3
4
5
6
7
8
9
10
11
12
const obj = {
a: 1,
b: 2,
c: 3,
};

const newObj = {
...obj,
};

console.log(newObj);
// { a: 1, b: 2, c: 3 }

... 符号很形象的表示了把对象中所有属性拿出来平铺的含义。说到平铺,Spread 放在函数参数时,也表示将对象中每个 properties 拿出来作为平铺参数:

1
2
3
4
5
6
7
const arr = [1, 2, 3];

const sum = (a, b, c) => a + b + c;

console.log(sum(...arr)); // Outputs: 6
// ^
// sum(1, 2, 3)

Rest

... 作 Rest 含义时,表示将多个值收集为一个数组,如用在函数定义的位置:

1
2
3
4
5
6
7
8
const sum = (...args) => {
return args.reduce((acc, curr) => acc + curr, 0);
// ^
// [1, 2, 3]
};

console.log(sum(1, 2, 3));
// 6

当然也可以在 ... 前面放置其他变量,这样 ... 仅聚合剩余的变量。... 之后不能再定义变量或者 ...

1
2
3
4
5
6
7
8
const sum = (a, b, ...restOfArguments) => {
return a + b + restOfArguments.reduce((acc, curr) => acc + curr, 0);
// ^ ^ ^
// 1 2 [3, 4, 5]
};

console.log(sum(1, 2, 3, 4, 5));
// 15

精读

Rest 处理 Set 与 Map

SetMap 都可以通过数组模式赋初值:

1
2
3
4
5
6
const mySet = new Set(["a", "b", "c"]);
const myMap = new Map([
["a", 1],
["b", 2],
["c", 3],
]);

... 符号作 Rest 用途时,可以将其解构为数组:

1
2
[...mySet] // ['a', 'b', 'c']
[...myMap] // [ ['a', 1], ['b', 2], ['c', 3] ]

特别的,Map 与 Set 仅支持数组方式解构,不支持对象模式解构:

1
2
{...mySet} // {}
{...myMap} // {}

但对于一个普通数组,是同时支持数组与对象模式解构的:

1
2
3
const arr = ['a', 'b', 'c']
[...arr] // ['a', 'b', 'c']
{...arr} // {0: 'a', 1: 'b', 2: 'c'}

这是因为数组变量有潜在的下标,这些下标可以转换为对象的 Key,而 Map Set 不存在下标,所以转换为对象找不到 Key,因此就不支持对象模式的解构。

更具体的原因与对象的可迭代性有关,虽然 MapSet 都支持迭代,但如果用 for key of 来测试,会发现它们的 key 是 undefined

Spread 会丢失 get() 与 set()

Spread 并不代表完整复制整个对象,它能拷贝这个对象属性定义中的瞬时值,比如:

1
2
3
4
5
6
7
const obj = {
a: 1,
get b() {
return 2;
},
};
const newObj = { ...obj };

newObj.b 属性不再是 get() 方法,而是固定值 2,这在 get() 函数内返回非固定值,或希望懒加载代码时会产生问题。

究其原因,Spread 毕竟不是在定义对象,更恰当的理解应该是 “访问对象”,所以访问的结果就是执行 get()

Rest 会跳过不可枚举属性

1
2
const err = new Error('error')
{...error} // {}

Error 拥有两个不可枚举属性 messagestack,所以不会被 Rest 收集到,遇到这种场景可以使用其他方式,如直接访问 error.message

总结

... 用在赋值位置含义为 Spread,用在参数收集位置含义为 Rest,同时因为该语法写起来很简单,因此有一些默认逻辑小心不要掉坑里,比如默认会执行对象属性的 getter,会跳过不可枚举属性等。

讨论地址是:精读《Rest vs Spread 语法》· Issue ##447 · dt-fe/weekly

如果你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

关注 前端精读微信公众号

版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证


本站由 钟意 使用 Stellar 1.28.1 主题创建。
又拍云 提供CDN加速/云存储服务
vercel 提供托管服务
湘ICP备2023019799号-1
总访问 次 | 本页访问