1. 程式人生 > >JS: Object.assign() Vs Spread Operator

JS: Object.assign() Vs Spread Operator

昨天寫了一篇關於 Spread Operator 應用在 Object 上的博文,但是最後舉例 Object.assign()... 區別時,有些混淆,今天查了些資料,作為總結。

...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 }
複製程式碼
參考

2ality.com/2016/10/res…