JavaScript … 延展操作符(Spread operator)

上一篇文章 解决 Node.js 项目报 SyntaxError: Unexpected token … 错误 - 干志雄的博客 就是因为…操作符导致运行报错了,本文将介绍…操作符和解答上一篇文章中的思考问题。

… 操作符英文叫 Spread operator,即延展操作符。该操作符从 ES6 开始支持。ES9 中也新增了些特性。

ES6 延展操作符(Spread operator)的特性

ES6 即 ECMAScript 2015 规范。

延展操作符…,从ES6开始添加的

  • 可以在函数调用/数组构造时, 将数组表达式或者 string 在语法层面展开;
  • 还可以在构造对象时, 将对象表达式按key-value的方式展开。

语法

函数调用

1
myFunction(...iterableObj);

数组构造或字符串:

1
2
3
var iterableObj = [0, 1, 2, 3];
[...iterableObj, '4', ...'hello', 6];
(11) [0, 1, 2, 3, '4', 'h', 'e', 'l', 'l', 'o', 6]

应用场景

在函数调用时使用延展操作符

1
2
3
4
5
6
7
8
9
10
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];

//不使用延展操作符
console.log(sum.apply(null, numbers));// 6

//使用延展操作符
console.log(sum(...numbers));// 6
Rest 参数和扩展运算符

ES2015 引入了 Rest 参数和扩展运算符。三个点(…)仅用于数组。Rest 参数语法允许我们将一个不定数量的参数表示为一个数组。

1
2
3
4
5
6
7
8
9
10
function restParam(p1, p2, ...p3) {
console.log(p1);
// p1 = 1
console.log(p2);
// p2 = 2
console.log(p3);
// p3 = [3, 4, 5]
}

restParam(1, 2, 3, 4, 5);
展开操作符

展开操作符以相反的方式工作,将数组转换成可传递给函数的单独参数。例如Math.max()返回给定数字中的最大值:

1
2
3
4
5
/**
* Returns the larger of a set of supplied numeric expressions.
* @param values Numeric expressions to be evaluated.
*/
max(...values: number[]): number;
1
2
const values = [99, 100, -1, 48, 16];
console.log( Math.max(...values) ); // 100

构造数组

没有展开语法的时候,只能组合使用 push,splice,concat 等方法,来将已有数组元素变成新数组的一部分。有了展开语法, 构造新数组会变得更简单、更优雅。

和参数列表的展开类似, … 在构造字数组时, 可以在任意位置多次使用。

1
2
3
const stuendts = ['Jine','Tom']; 
const persons = ['Tony',... stuendts,'Aaron','Anna'];
conslog.log(persions)// ["Tony", "Jine", "Tom", "Aaron", "Anna"]

连接多个数组

1
2
3
4
5
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = [...arr1, ...arr2];// 将 arr2 中所有元素附加到 arr1 后面并返回
//等同于
var arr4 = arr1.concat(arr2);

数组拷贝

展开语法和 Object.assign() 行为一致, 执行的都是浅拷贝(只遍历一层)。

1
2
3
4
var arr = [1, 2, 3];
var arr2 = [...arr]; // 等同于 arr.slice()
arr2.push(4);
console.log(arr2)//[1, 2, 3, 4]

在React中的应用

通常我们在封装一个组件时,会对外公开一些 props 用于实现功能。大部分情况下在外部使用都应显示的传递 props 。但是当传递大量的props时,会非常繁琐,这时我们可以使用 …(延展操作符,用于取出参数对象的所有可遍历属性) 来进行传递。

  1. 一般情况下我们应该这样写。

    1
    <CustomComponent name ='Jine' age ={21} />
  2. 使用 … ,等同于上面的写法。

    1
    2
    3
    4
    5
    const params = {
    name: 'Jine',
    age: 21
    }
    <CustomComponent {...params} />
  3. 配合解构赋值避免传入一些不需要的参数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var params = {
    name: '123',
    title: '456',
    type: 'aaa'
    }

    var { type, ...other } = params;

    <CustomComponent type='normal' number={2} {...other} />
    //等同于
    <CustomComponent type='normal' number={2} name='123' title='456' />

ES9延展操作符(Spread operator)的特性

ES9 即 ECMAScript 2018 规范。

ES9 中 Spread operator 有关联的特性是:

  • object spread properties,即延展操作符增加了对对象的支持。
  • object rest properties,即 Rest 参数添加了对对象的支持。

语法

构造对象时,进行克隆或者属性拷贝。

但是这只是一个对象的浅拷贝。因此,如果一个对象 A 的属性是对象 B ,那么在克隆后的对象 cloneB 中,该属性指向对象 B。

1
2
3
4
> let a = 'a'; let b = {...a}
undefined
> b
{ '0': 'a' }

1
2
3
4
> let array = ['a', 'b', 'c']; let copyArray = {...array}
undefined
> copyArray
{ '0': 'a', '1': 'b', '2': 'c' }
1
2
3
4
5
6
7
8
9
10
11
12
>  let obj = {a: 0, b: 1, c: 2}; let objClone = {...obj};
undefined
> objClone
{ a: 0, b: 1, c: 2 }
> obj == objClone
false
> obj[a] = -1
-1
> obj
{ a: -1, b: 1, c: 2 }
> objClone
{ a: 0, b: 1, c: 2 }

合并对象

1
2
3
4
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var mergedObj = { ...obj1, ...obj2 };
// 合并后的对象: { foo: "baz", x: 42, y: 13 }

对象解构

提供了和数组一样的Rest参数()和展开操作符。

1
2
3
4
5
6
7
8
9
const myObject = {
a: 1,
b: 2,
c: 3
};

const { a, ...x } = myObject;
// a = 1
// x = { b: 2, c: 3 }

或者你可以使用它给函数传递参数:

1
2
3
4
5
6
7
8
9
10
11
12
function restParam({ a, ...x }) {
console.log(a);
// a = 1
console.log(x);
// x = { b: 2, c: 3 }
}

restParam({
a: 1,
b: 2,
c: 3
});

跟数组一样,Rest 参数只能在声明的结尾处使用。
此外,它只适用于每个对象的顶层,如果对象中嵌套对象则无法适用。

如何知道node的版本是否支持ES特性

通过 node.green 这个网站可以查询到 ES 特性是否支持 node 版本。

例如,我现在用的 node 版本为 v6.9.2,我要判断是否支持 object spread properties 这个特性?
其实有两个方法。

通过node.green查找

通过 node.green,找到 object spread properties,可以看到只有 8.6 及以上的版本才支持。

通过代码测试验证

  • 在 node 版本为 6.9.2 下验证。

    可以看到let b = {...a}输入回车后,只有三个.,没有任何提示了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    $ node 
    > let a = 'a'
    undefined
    > let b = {...a}
    ...
    > b
    ReferenceError: b is not defined
    at repl:1:1
    at sigintHandlersWrap (vm.js:22:35)
    at sigintHandlersWrap (vm.js:96:12)
    at ContextifyScript.Script.runInThisContext (vm.js:21:12)
    at REPLServer.defaultEval (repl.js:313:29)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:513:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    >
  • 在 node 版本为 10.16.3 下验证。

    木有问题,测试通过。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # ganzhixiong @ ganzhixiongdeMacBook-Pro-3 in ~/Documents/blog on git:master x [23:42:57] 
    $ node -v
    v10.16.3

    # ganzhixiong @ ganzhixiongdeMacBook-Pro-3 in ~/Documents/blog on git:master x [23:43:01]
    $ node
    > let a = 'a'
    undefined
    > let b = {...a}
    undefined
    > b
    { '0': 'a' }
    >

参考链接


相信你看完本文,应该已经知道上一篇文章 解决 Node.js 项目报 SyntaxError: Unexpected token … 错误 - 干志雄的博客 最后留下的思考问题的答案了。

JavaScript … 延展操作符(Spread operator)

https://ganzhixiong.com/p/bda54489/

Author

干志雄

Posted on

2021-10-04

Updated on

2021-10-04

Licensed under

Comments