你要知道的 - Spread Operator for objects 技巧

今天看到了你必須收藏的 ES6 語法密糖 - Spread Operator 技巧,這篇文章,收穫不少,同時也想起來 ... 也有一些操做對象的用法,總結了一下。html

在 ECMAScript 2018 中 Spread Operator 增長了對對象的支持,使得它的應用更爲普遍,本文重點介紹如何將它與 Object 一塊兒使用以及與 Object.assgin 的區別。es6

能夠經過BABEL,查看示例代碼 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}
複製代碼
... 刪除屬性值

利用 ... 來刪除對象中的某一個屬性post

let { x: deleted, ...y } = { x: 1, y: 2, a: 3, b: 4 };
console.log(y); // {y: 2, a: 3, b: 4}
複製代碼
... 複製對象

在 JavaScript 中,有一個常見賦值語法以下ui

var cat = { age: 4 };
var kitten = cat;
kitten.age = 1;
複製代碼

此時, catkitten 引用同一個對象,若是修改了 kitten 的屬性,相應的 cat 也會發生變化。spa

console.log(kitten.age); // 1
console.log(cat.age); // 1 <-- problem!
複製代碼

使用 Spread Operator 能夠輕鬆地建立一個具備現有對象的全部相同屬性的新對象。prototype

const cat = { age: 4 };
const kitten = { ...cat }; // <-- changed
kitten.age = 1;

console.log(kitten.age); // 1
console.log(cat.age); // 4 <-- fixed!
複製代碼

可是,利用 Spread Operator 去賦值對象,只能完成淺複製,也就是說利用 ... 去複製對象時,並不能遞歸地複製全部層級。rest

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 建立的對象上code

const cat = { legs: 4 };
const dog = {
    ...cat,
    sound: "woof"
};
console.log(cat); // { legs: 4 }
console.log(dog); // { legs: 4, sound: "woof" }
複製代碼

一樣,能夠看到 cat 對象未被更改,但新 dog 對象具備來自 catlegs 屬性以及新 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" }
複製代碼

上述 ...catsound: "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,而 setterundeined

    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 
    }
    複製代碼

    上述結果代表,在獲得的 clone1clone2中,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 }
複製代碼
參考
相關文章
相關標籤/搜索