對(duì)象的擴(kuò)展運(yùn)算符(...)用于取出參數(shù)對(duì)象的所有可遍歷屬性,拷貝到當(dāng)前對(duì)象之中。
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
由于數(shù)組是特殊的對(duì)象,所以對(duì)象的擴(kuò)展運(yùn)算符也可以用于數(shù)組。
let foo = { ...['a', 'b', 'c'] };
foo
// {0: "a", 1: "b", 2: "c"}
如果擴(kuò)展運(yùn)算符后面是一個(gè)空對(duì)象,則沒有任何效果。
{...{}, a: 1}
// { a: 1 }
如果擴(kuò)展運(yùn)算符后面不是對(duì)象,則會(huì)自動(dòng)將其轉(zhuǎn)為對(duì)象。
// 等同于 {...Object(1)}
{...1} // {}
上面代碼中,擴(kuò)展運(yùn)算符后面是整數(shù)1,會(huì)自動(dòng)轉(zhuǎn)為數(shù)值的包裝對(duì)象Number{1}。由于該對(duì)象沒有自身屬性,所以返回一個(gè)空對(duì)象。
下面的例子都是類似的道理。
// 等同于 {...Object(true)}
{...true} // {}
// 等同于 {...Object(undefined)}
{...undefined} // {}
// 等同于 {...Object(null)}
{...null} // {}
但是,如果擴(kuò)展運(yùn)算符后面是字符串,它會(huì)自動(dòng)轉(zhuǎn)成一個(gè)類似數(shù)組的對(duì)象,因此返回的不是空對(duì)象。
{...'hello'}
// {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}
對(duì)象的擴(kuò)展運(yùn)算符等同于使用Object.assign()方法。
let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);
上面的例子只是拷貝了對(duì)象實(shí)例的屬性,如果想完整克隆一個(gè)對(duì)象,還拷貝對(duì)象原型的屬性,可以采用下面的寫法。
// 寫法一
const clone1 = {
__proto__: Object.getPrototypeOf(obj),
...obj
};
// 寫法二
const clone2 = Object.assign(
Object.create(Object.getPrototypeOf(obj)),
obj
);
// 寫法三
const clone3 = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
上面代碼中,寫法一的proto屬性在非瀏覽器的環(huán)境不一定部署,因此推薦使用寫法二和寫法三。
擴(kuò)展運(yùn)算符可以用于合并兩個(gè)對(duì)象。
let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);
如果用戶自定義的屬性,放在擴(kuò)展運(yùn)算符后面,則擴(kuò)展運(yùn)算符內(nèi)部的同名屬性會(huì)被覆蓋掉。
let aWithOverrides = { ...a, x: 1, y: 2 };
// 等同于
let aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
// 等同于
let x = 1, y = 2, aWithOverrides = { ...a, x, y };
// 等同于
let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 });
上面代碼中,a對(duì)象的x屬性和y屬性,拷貝到新對(duì)象后會(huì)被覆蓋掉。
這用來修改現(xiàn)有對(duì)象部分的屬性就很方便了。
let newVersion = {
...previousVersion,
name: 'New Name' // Override the name property
};
上面代碼中,newVersion對(duì)象自定義了name屬性,其他屬性全部復(fù)制自previousVersion對(duì)象。
如果把自定義屬性放在擴(kuò)展運(yùn)算符前面,就變成了設(shè)置新對(duì)象的默認(rèn)屬性值。
let aWithDefaults = { x: 1, y: 2, ...a };
// 等同于
let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
// 等同于
let aWithDefaults = Object.assign({ x: 1, y: 2 }, a);
與數(shù)組的擴(kuò)展運(yùn)算符一樣,對(duì)象的擴(kuò)展運(yùn)算符后面可以跟表達(dá)式。
const obj = {
...(x > 1 ? {a: 1} : {}),
b: 2,
};
擴(kuò)展運(yùn)算符的參數(shù)對(duì)象之中,如果有取值函數(shù)get,這個(gè)函數(shù)是會(huì)執(zhí)行的。
// 并不會(huì)拋出錯(cuò)誤,因?yàn)?x 屬性只是被定義,但沒執(zhí)行
let aWithXGetter = {
...a,
get x() {
throw new Error('not throw yet');
}
};
// 會(huì)拋出錯(cuò)誤,因?yàn)?x 屬性被執(zhí)行了
let runtimeError = {
...a,
...{
get x() {
throw new Error('throw now');
}
}
};