今天看到了你必須收藏的 ES6 語法密糖 - Spread Operator 技巧,這篇文章,收穫很多,同時也想起來
...
也有一些操作物件的用法,總結了一下。
在 ECMAScript 2018 中 Spread Operator 增加了對物件的支援,使得它的應用更為廣泛,本文重點介紹如何將它與 Object 一起使用以及與 Object.assgin
的區別。
可以通過BABEL,檢視示例程式碼 babel 編譯後的結果。
...
解構賦值
除去已經宣告的屬性,剩餘的所有屬性都會賦給 ...
後的屬性名
let { x, ...y } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // {y: 2, a: 3, b: 4}
複製程式碼
...
刪除屬性值
利用 ...
來刪除物件中的某一個屬性
let { x: deleted, ...y } = { x: 1, y: 2, a: 3, b: 4 };
console.log(y); // {y: 2, a: 3, b: 4}
複製程式碼
...
複製物件
在 JavaScript 中,有一個常見賦值語法如下
var cat = { age: 4 };
var kitten = cat;
kitten.age = 1;
複製程式碼
此時, cat
和 kitten
引用同一個物件,如果修改了 kitten
的屬性,相應的 cat
也會發生變化。
console.log(kitten.age); // 1
console.log(cat.age); // 1 <-- problem!
複製程式碼
使用 Spread Operator 可以輕鬆地建立一個具有現有物件的所有相同屬性的新物件。
const cat = { age: 4 };
const kitten = { ...cat }; // <-- changed
kitten.age = 1;
console.log(kitten.age); // 1
console.log(cat.age); // 4 <-- fixed!
複製程式碼
但是,利用 Spread Operator 去賦值物件,只能完成淺複製,也就是說利用 ...
去複製物件時,並不能遞迴地複製所有層級。
const cat = { age: 4, toys: ["mouse", "catnip"] };
const kitten = { ...cat };
// const kitten = Object.assign({}, cat); <-- same result
kitten.toys[1] = "yarn";
console.log(kitten.toys); // ["mouse", "yarn"]
console.log(cat.toys); // ["mouse", "yarn"] <-- problem!
複製程式碼
...
擴充套件物件
利用 ...
來擴充物件,就是將新屬性新增到使用 Spread Operator 建立的物件上
const cat = { legs: 4 };
const dog = {
...cat,
sound: "woof"
};
console.log(cat); // { legs: 4 }
console.log(dog); // { legs: 4, sound: "woof" }
複製程式碼
同樣,可以看到 cat
物件未被更改,但新 dog
物件具有來自 cat
的 legs
屬性以及新 sound
屬性,如果sound
已經存在的話,則會覆蓋。
const cat = { legs: 4, sound: "meow" };
const dog = {
...cat,
sound: "woof"
};
console.log(cat); // { legs: 4, sound: "meow" }
console.log(dog); // { legs: 4, sound: "woof" }
複製程式碼
但是,使用 ...
擴充物件時,要注意行順序,也就是
const cat = { legs: 4, sound: "meow" };
const dog = {
sound: "woof",
...cat
};
console.log(cat); // { legs: 4, sound: "meow" }
console.log(dog); // { legs: 4, sound: "meow" }
複製程式碼
上述 ...cat
將 sound: "woof"
改寫為 sound: "meow"
。
...
與 Object.assign
的區別
在上述利用 ...
處理物件的過程中,會發現 ...
有些時候與 Object.assgin
的操作近乎與等價的,那麼他們具體的區別是什麼。
...
和 Object.assign()
整體的用法非常下關係,主要區別在於 Object.assign()
函式會觸發 setters,而 ...
語法則不會,也就是說 ...
是定義了新屬性,而 Object.assign()
則是設定了它們。
Object.assign()
的基本用法
-
改變原有物件
Object.assign(target, source1, source2); 複製程式碼
target
已經被修改,source1
以及source2
會被複制到其中。 -
建立新的物件
const result = Object.assign({}, source1, source2); 複製程式碼
result
是一個新的物件,source1
以及source2
被複制到其中。
在第二種方法上,...
與 Object.assign()
是非常類似的。接下來,闡述它們之間具體的相似點和不同點。
Object.assign
與 ...
的相同點
-
...
和Object.assign()
都是通過get
運算子來取值在將它們寫入目標之前,這兩個操作都會使用
get
操作從源物件讀取相應的屬性值。因此,在此過程中,getter
將轉換為正常的資料屬性,具體如下const original = { get foo() { console.log('getter'); return 123; } }; 複製程式碼
original
物件有getter foo
,而setter
為undeined
Object.getOwnPropertyDescriptor(original, 'foo') /* log { get: [Function: foo], set: undefined, enumerable: true, configurable: true } */ 複製程式碼
但是,利用
Object.assgin()
以及...
對original
物件進行克隆時,會發現const clone1 = {...original}; // 觸發 original 的 getter 會 log "getter" Object.getOwnPropertyDescriptor(clone1, 'foo'); /* log getter 以及被轉換為正常的資料屬性 { value: 123, writable: true, enumerable: true, configurable: true } */ const clone2 = Object.assign({}, original); // 觸發 original 的 getter 會 log "getter" Object.getOwnPropertyDescriptor(clone2, 'foo') { value: 123, writable: true, enumerable: true, configurable: true } 複製程式碼
上述結果表明,在得到的
clone1
和clone2
中,foo
只是一個普通的資料屬性(它的屬性描述符具有屬性值和可寫); -
...
和Object.assign
只會處理可列舉資料這兩個操作都會忽略所有繼承的屬性和所有不可列舉的屬性。
const proto = { inheritedEnumerable: 1, }; const obj = Object.create(proto, { ownEnumerable: { value: 2, enumerable: true, }, ownNonEnumerable: { value: 3, enumerable: false, }, }); console.log(obj); // { ownEnumerable: 2, ownNonEnumerable: 3, __proto__: { inheritedEnumerable: 1 } } console.log({ ...obj }); // { ownEnumerable: 2 } console.log(Object.assign({}, obj)); // { ownEnumerable: 2 } 複製程式碼
Object.assign
與 ...
的不同點
它們的不同點在於 ...
會定義屬性,而 Object.assign()
會設定它們,也就是說 ...
定義了目標物件中的新屬性,Object.assign()
則是使用 set
操作符進行寫入。
Object.defineProperty(Object.prototype, 'foo', {
set(value) {
console.log('SET', value);
},
});
const obj = {foo: 123};
console.log(Object.assign({}, obj));
// 會觸發 set, log SET 123
// log {}
console.log({ ...obj });
// log { foo: 123 }
複製程式碼