ES6——扩展运算符/三点运算符(...)

扩展运算符(spread)是三个点(...)。

数组的扩展运算符

对于数组来说,它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列

console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5

该运算符主要用于函数调用。

function push(array, ...items) {
  array.push(...items);
}

function add(x, y) {
  return x + y;
}

const numbers = [4, 38];
add(...numbers) // 42

上面代码中,array.push(...items)add(...numbers)这两行,都是函数的调用,它们的都使用了扩展运算符。该运算符将一个数组,变为参数序列。

扩展运算符与正常的函数参数可以结合使用,非常灵活。

function f(v, w, x, y, z) { }
const args = [0, 1];
f(-1, ...args, 2, ...[3]);

扩展运算符后面还可以放置表达式。

const arr = [
  ...(x > 0 ? ['a'] : []),
  'b',
];

如果扩展运算符后面是一个空数组,则不产生任何效果。

[...[], 1]
// [1]

注意,只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。

(...[1, 2])
// Uncaught SyntaxError: Unexpected number

console.log((...[1, 2]))
// Uncaught SyntaxError: Unexpected number

console.log(...[1, 2])
// 1 2

上面三种情况,扩展运算符都放在圆括号里面,但是前两种情况会报错,因为扩展运算符所在的括号不是函数调用。

扩展运算符的应用

复制数组:

var arr1 = [1, 2];
var arr2 = [...arr1];

console.log(arr2);		//[1, 2]

合并数组:

var arr1 = [1, 2];
var arr2 = [3, 4];
var arr3 = [...arr1, ...arr2];

console.log(arr3);		//[1, 2, 3, 4]

扩展运算符可以与解构赋值结合起来,用于生成数组:

var [a, ...b] = [1, 2, 3];
console.log(a);			//1
console.log(b);			//[2, 3]

var [c, ...d] = [];
console.log(c);			//undefined
console.log(d);			//[]

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

var [...a, b] = [1, 2, 3];			//报错
var [c, ...d, e] = [4, 5, 6];		//报错

扩展运算符还可以将字符串转为真正的数组。

[...'hello']
//['h', 'e', 'l', 'l', 'o']

任何定义了遍历器(Iterator)接口的对象(参阅 Iterator 一章),都可以用扩展运算符转为真正的数组。

对象的扩展运算符

对于对象来说,它可以取出参数对象的所有可遍历属性,拷贝到当前对象之中。

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

由于数组是特殊的对象,所以对象的扩展运算符也可以用于数组。

let foo = { ...['a', 'b', 'c'] };
foo
// {0: "a", 1: "b", 2: "c"}

如果扩展运算符后面是一个空对象,则没有任何效果。

{...{}, a: 1}
// { a: 1 }

如果扩展运算符后面不是对象,则会自动将其转为对象。

//等同于 {...Object(1)}
{...1} //{}

上面代码中,扩展运算符后面是整数1,会自动转为数值的包装对象Number{1}。由于该对象没有自身属性,所以返回一个空对象。

下面的例子都是类似的道理。

// 等同于 {...Object(true)}
{...true} // {}

// 等同于 {...Object(undefined)}
{...undefined} // {}

// 等同于 {...Object(null)}
{...null} // {}

但是,如果扩展运算符后面是字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象。

{...'hello'}
// {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}

对象的扩展运算符等同于使用Object.assign()方法。

var obj1 = { ...obj };
// 等同于
var obj2 = Object.assign({}, obj);

上面的例子只是拷贝了对象实例的属性,如果想完整克隆一个对象,还拷贝对象原型的属性,可以采用下面的写法。

// 写法一
var obj1 = {
  __proto__: Object.getPrototypeOf(obj),
  ...obj
};

// 写法二
var obj2 = Object.assign(
  Object.create(Object.getPrototypeOf(obj)),
  obj
);

// 写法三
var obj3 = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
)

扩展运算符可以用于合并两个对象。

let obj = { ...obj1, ...obj2 };
// 等同于
let obj = Object.assign({}, obj1, obj2);

如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。

var obj = {a: 1, b: 2};

var obj1 = {...obj, a: 3, b:4};		//obj1 {a: 3, b: 4}
// 等同于
var obj2 = {...obj, ...{a: 3, b: 4}};
// 等同于
var a = 3, b = 4, obj3 = {...obj, a, b};
// 等同于
var obj4 = Object.assign({}, obj1, {x: 1, y: 2});

与数组的扩展运算符一样,对象的扩展运算符后面可以跟表达式。

var obj = {...(2 > 1 ? {a: 1} : {}), b: 2};
//{a: 1, b: 2}

扩展运算符可以与解构赋值使用,将目标对象自身的所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。

var {a, b, ...c} = {a: 1, b: 2, x: 3, y: 4};
a	//1
b	//2
c	//{x: 3, y: 4}

解构赋值必须是最后一个参数,否则会报错。

var {...a, b} = {x: 1, y: 2, b: 3};		//报错

扩展运算符的解构赋值,不能复制继承自原型对象的属性。

var obj1 = {a: 1};
var obj2 = {b: 2};
obj2.__proto__ = obj1;
var {...obj3} = obj2;
obj3	//{b: 2}

你可能感兴趣的:(ES6/7/8/9)