forked from ascoders/weekly
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
144 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
符号 `...` 在 JS 语言里同时被用作 Rest 与 Spread 两个场景,本周我们就结合 [Rest vs Spread syntax in JavaScript](https://www.amitmerchant.com/rest-vs-spread-syntax-in-javascript/) 聊聊这两者的差异以及一些坑。 | ||
|
||
## 概述 | ||
|
||
### Spread | ||
|
||
`...` 作为 Spread 含义时,效果为扩散对象的属性: | ||
|
||
```ts | ||
const obj = { | ||
a: 1, | ||
b: 2, | ||
c: 3, | ||
}; | ||
|
||
const newObj = { | ||
...obj, | ||
}; | ||
|
||
console.log(newObj); | ||
// { a: 1, b: 2, c: 3 } | ||
``` | ||
|
||
`...` 符号很形象的表示了把对象中所有属性拿出来平铺的含义。说到平铺,Spread 放在函数参数时,也表示将对象中每个 properties 拿出来作为平铺参数: | ||
|
||
```ts | ||
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 含义时,表示将多个值收集为一个数组,如用在函数定义的位置: | ||
|
||
```ts | ||
const sum = (...args) => { | ||
return args.reduce((acc, curr) => acc + curr, 0); | ||
// ^ | ||
// [1, 2, 3] | ||
}; | ||
|
||
console.log(sum(1, 2, 3)); | ||
// 6 | ||
``` | ||
|
||
当然也可以在 `...` 前面放置其他变量,这样 `...` 仅聚合剩余的变量。`...` 之后不能再定义变量或者 `...`: | ||
|
||
```ts | ||
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 | ||
|
||
`Set` 与 `Map` 都可以通过数组模式赋初值: | ||
|
||
```ts | ||
const mySet = new Set(["a", "b", "c"]); | ||
const myMap = new Map([ | ||
["a", 1], | ||
["b", 2], | ||
["c", 3], | ||
]); | ||
``` | ||
|
||
在 `...` 符号作 `Rest` 用途时,可以将其解构为数组: | ||
|
||
```ts | ||
[...mySet] // ['a', 'b', 'c'] | ||
[...myMap] // [ ['a', 1], ['b', 2], ['c', 3] ] | ||
``` | ||
|
||
特别的,Map 与 Set 仅支持数组方式解构,不支持对象模式解构: | ||
|
||
```ts | ||
{...mySet} // {} | ||
{...myMap} // {} | ||
``` | ||
|
||
但对于一个普通数组,是同时支持数组与对象模式解构的: | ||
|
||
```ts | ||
const arr = ['a', 'b', 'c'] | ||
[...arr] // ['a', 'b', 'c'] | ||
{...arr} // {0: 'a', 1: 'b', 2: 'c'} | ||
``` | ||
|
||
这是因为数组变量有潜在的下标,这些下标可以转换为对象的 Key,而 `Map` `Set` 不存在下标,所以转换为对象找不到 Key,因此就不支持对象模式的解构。 | ||
|
||
更具体的原因与对象的可迭代性有关,虽然 `Map` 与 `Set` 都支持迭代,但如果用 `for key of` 来测试,会发现它们的 key 是 `undefined`。 | ||
|
||
### Spread 会丢失 get() 与 set() | ||
|
||
Spread 并不代表完整复制整个对象,它能拷贝这个对象属性定义中的瞬时值,比如: | ||
|
||
```ts | ||
const obj = { | ||
a: 1, | ||
b: get() { return 2 } | ||
} | ||
const newObj = { ...obj } | ||
``` | ||
|
||
`newObj.b` 属性不再是 `get()` 方法,而是固定值 `2`,这在 `get()` 函数内返回非固定值,或希望懒加载代码时会产生问题。 | ||
|
||
究其原因,Spread 毕竟不是在定义对象,更恰当的理解应该是 “访问对象”,所以访问的结果就是执行 `get()`。 | ||
|
||
### Rest 会跳过不可枚举属性 | ||
|
||
```ts | ||
const err = new Error('error') | ||
{...error} // {} | ||
``` | ||
|
||
`Error` 拥有两个不可枚举属性 `message` 与 `stack`,所以不会被 Rest 收集到,遇到这种场景可以使用其他方式,如直接访问 `error.message`。 | ||
|
||
## 总结 | ||
|
||
`...` 用在赋值位置含义为 Spread,用在参数收集位置含义为 Rest,同时因为该语法写起来很简单,因此有一些默认逻辑小心不要掉坑里,比如默认会执行对象属性的 `getter`,会跳过不可枚举属性等。 | ||
|
||
> 讨论地址是:[精读《Rest vs Spread 语法》· Issue #447 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/447) | ||
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。** | ||
|
||
> 关注 **前端精读微信公众号** | ||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg"> | ||
|
||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)) |