前言
ES提案中的Rest/Spread Properties
- 剩余部分操作符(...),仅用于数据解构和函数参数定义
- 扩散操作符(...),仅用于数组字面量和函数以及方法调用的时候
对象解构中的剩余操作符
可以这么用了
const obj = {foo: 1, bar: 2, baz: 3};
const {foo, ...rest} = obj;
// Same as:
// const foo = 1;
// const rest = {bar: 2, baz: 3};
也可以这么用了
function func({param1, param2, ...rest}) { // rest operator
console.log('All parameters: ',
{param1, param2, ...rest}); // spread operator
return param1 + param2;
}
感觉叼叼的
语法限制
对象中每一层级(...)操作符只能用一次,并且只能放在最后
const {...rest, foo} = obj; // SyntaxError
const {foo, ...rest1, ...rest2} = obj; // SyntaxError
你可以这样用:
const obj = {
foo: {
a: 1,
b: 2,
c: 3,
},
bar: 4,
baz: 5,
};
const {foo: {a, ...rest1}, ...rest2} = obj;
// Same as:
// const a = 1;
// const rest1 = {b: 2, c: 3};
// const rest2 = {bar: 4, baz: 5};
对象字面量中的扩散操作符
在对象字面量中,可以通过...将A对象的所有可以枚举的属性插入到B对象中
const A= {foo: 1, bar: 2, baz: 3};
const B = {...obj, qux: 4}
// B: { foo: 1, bar: 2, baz: 3, qux: 4 }
需要注意的是如果属性名正好相同的话,是会相互覆盖的,覆盖的结果取决于变量的顺序。
const obj = {foo: 1, bar: 2, baz: 3};
{...obj, foo: true}
//{ foo: true, bar: 2, baz: 3 }
{foo: true, ...obj}
//{ foo: 1, bar: 2, baz: 3 }
使用场景及注意事项
克隆对象
只会克隆其可枚举的属性,不会克隆其原型指向,这个跟Object.assign
很像
如果想克隆其原型,在浏览器中可以使用如下的方法
const clone1 = {__proto__: Object.getPrototypeOf(obj), ...obj};
const clone2 = Object.assign(
Object.create(Object.getPrototypeOf(obj)), obj);
整体克隆
有的场景下,你需要拷贝对象的所有属性:包括writable,enumerable,getters,setters等等,这个时候Object.assign()和...操作符都没有无能为力,可以使用
const clone1 = Object.defineProperties({},
Object.getOwnPropertyDescriptors(obj));
如果还想保留克隆对象的原型的话,可以使用Object.create()
const clone2 = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj));
浅拷贝
这些克隆操作都是浅拷贝,需要注意避免可能造成的问题
其他用途
合并对象
const merged = {...obj1, ...obj2}; const merged = Object.assign({}, obj1, obj2);
附加默认值
const DEFAULTS = {foo: 'a', bar: 'b'};
const userData = {foo: 1};
const data = {...DEFAULTS, ...userData};
const data = Object.assign({}, DEFAULTS, userData);
制定个别键的默认值
const userData = {foo: 1};
const data = {foo: 'a', bar: 'b', ...userData};
const data = Object.assign({}, {foo:'a', bar:'b'}, userData); // {foo: 1, bar: 'b'}
扩散操作符和Object.assign()
的异同
两种使用Object.assign()
的方式
Object.assign(target, source1, source2);
这个情况下,target被改变了
const result = Object.assign({}, source1, source2);
这个情况下,没有对现存的对象做任何改变
共同点
扩散操作符和Object.assign()
都是使用get
操作去读取value的
扩散操作符和Object.assign()
都只能获取枚举属性
不同点
扩散操作符定义了属性,Object.assign()
设置了属性
扩散操作符在目标对象定义了新的属性,Object.assign()
是通过set
操作穿件了属性,有两种结果:
第一: Object.assign()
会触发setters
Object.defineProperty(Object.prototype, 'foo', {
set(value) {
console.log('SET', value);
},
});
const obj = {foo: 123};
Object.assign({}, obj)
//SET 123
//{}
{ ...obj }
//{ foo: 123 }
第二: 如果目标对象设置了只读属性,Object.assign()
会继承只读属性,扩散操作符不会
Object.defineProperty(Object.prototype, 'bar', {
writable: false,
value: 'abc',
});
const tmp = {};
tmp.bar = 123;
// 在严格模式下报错:TypeError: Cannot assign to read only property 'bar'
// 在非严格模式下只是忽略,不报错