[轉]理解Object.assign


本節內容我們繼續探討關於ES2015的一些新的內容,Object.assign函數的使用,使用該函數我們可以快速的復制一個或者多個對象到目標對象中,本文內容涉及es6,es7相關的對象復制的內容,以及一些es5的替代方案的介紹。

函數原型

首先看一下函數的定義: 函數參數為一個目標對象(該對象作為最終的返回值),源對象(此處可以為任意多個)。通過調用該函數可以拷貝所有可被枚舉的自有屬性值到目標對象中。

Object.assign(target, ...sources)

這里我們需要強調的三點是:

  1. 可被枚舉的屬性
  2. 自有屬性
  3. string或者Symbol類型是可以被直接分配的

拷貝過程中將調用源對象的getter方法,並在target對象上使用setter方法實現目標對象的拷貝。

函數實例

這里我們通過幾個MDN上的例子來介紹一下使用方法:

實例一

我們參考上面的原型函數說明即可知道其最開始的o1因為設置為target,則調用其setter方法設置了其他對象的屬性到自身。

var o1 = { a: 1 }; var o2 = { b: 2 }; var o3 = { c: 3 }; var obj = Object.assign(o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 } console.log(o1); // { a: 1, b: 2, c: 3 }, target object itself is changed.

實例二

我們自定義了一些對象,這些對象有一些包含了不可枚舉的屬性,另外注意使用 Object.defineProperty 初始化的對象默認是不可枚舉的屬性。對於可枚舉的對象我們可以直接使用Object.keys()獲得,或者使用for-in循環遍歷出來.

對於不可枚舉的屬性,使用Object.assign的時候將被自動忽略。

var obj = Object.create({ foo: 1 }, { // foo is an inherit property. bar: { value: 2 // bar is a non-enumerable property. }, baz: { value: 3, enumerable: true // baz is an own enumerable property. } }); var copy = Object.assign({}, obj); console.log(copy); // { baz: 3 } 

實例三

對於只讀的屬性,當分配新的對象覆蓋他的時候,將拋出異常:

var target = Object.defineProperty({}, 'foo', { value: 1, writable: false }); Object.assign(target, { bar: 2 }) //{bar: 2, foo: 1} Object.assign(target, { foo: 2 }) //Uncaught TypeError: Cannot assign to read only property 'foo' of object '#<Object>'(…)

Polyfill

這里我們簡單的看下如何實現es5版本的Object.assign:

實現步驟:

  1. 判斷是否原生支持該函數,如果不存在的話創建一個立即執行函數,該函數將創建一個assign函數綁定到Object上。
  2. 判斷參數是否正確(目的對象不能為空,我們可以直接設置{}傳遞進去,但必須設置該值)
  3. 使用Object在原有的對象基礎上返回該對象,並保存為out
  4. 使用for…in循環遍歷出所有的可枚舉的自有對象。並復制給新的目標對象(hasOwnProperty返回非原型鏈上的屬性)

源碼如下:

 if (typeof Object.assign != 'function') { (function () { Object.assign = function (target) { 'use strict'; if (target === undefined || target === null) { throw new TypeError('Cannot convert undefined or null to object'); } var output = Object(target); for (var index = 1; index < arguments.length; index++) { var source = arguments[index]; if (source !== undefined && source !== null) { for (var nextKey in source) { if (source.hasOwnProperty(nextKey)) { output[nextKey] = source[nextKey]; } } } } return output; }; })(); }

擴展內容

1.深度復制

當我們調用下面的函數的時候,由於Object.assign將覆蓋之前的內容,所以並不能完全的做到融合對象,而是全部替換掉,所以返回的對象內容將變成最后一個值; {a: {c: 3}

Object.assign({a: {b: 0}}, {a: {b: 1, c: 2}}, {a: {c: 3}});

如何深層次的融合對象,比如我們期望的輸出結果為:

{a:{b:1,c:3}}

這樣我們必須實現自己的算法來完成深層復制了,不過github上已經有很多好的解決方案,比如deep-merge 通過遞歸的方式逐層的去調用assign函數。

2.ES2016實現

在es7中我們使用rest屬性可以捕獲所有剩余的對象內容比如下面的例子(可使用babel-repl頁面測試,瀏覽器一般尚未支持):

let { fname, lname, ...rest } = { fname: "Hemanth", lname: "HM", location: "Earth", type: "Human" }; fname; //"Hemanth" lname; //"HM" rest; // {location: "Earth", type: "Human"}

這樣我們就可以使用該特性來實現assign函數

let oldObj1={a:"a",b:{b1:"b1"}} let oldObj2={a:"a1",b:{b2:"b2"},c:"c"} let newObject={...oldObj1,...oldObj2}; console.log(newObject) {"a":"a1","b":{"b2":"b2"},"c":"c"}

不過仍舊只是淺層的替換,並沒有實現深層次的合並。

 

原文地址: https://cnodejs.org/topic/56c49662db16d3343df34b13


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM