本節內容我們繼續探討關於ES2015的一些新的內容,Object.assign函數的使用,使用該函數我們可以快速的複製一個或者多個對象到目標對象中,本文內容涉及es6,es7相關的對象複製的內容,以及一些es5的替代方案的介紹。 函數原型 首先看一下函數的定義: 函數參數為一個目標對象(該對象作為最 ...
本節內容我們繼續探討關於ES2015的一些新的內容,Object.assign函數的使用,使用該函數我們可以快速的複製一個或者多個對象到目標對象中,本文內容涉及es6,es7相關的對象複製的內容,以及一些es5的替代方案的介紹。
函數原型
首先看一下函數的定義: 函數參數為一個目標對象(該對象作為最終的返回值),源對象(此處可以為任意多個)。通過調用該函數可以拷貝所有可被枚舉的自有屬性值到目標對象中。
Object.assign(target, ...sources)
這裡我們需要強調的三點是:
- 可被枚舉的屬性
- 自有屬性
- 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:
實現步驟:
- 判斷是否原生支持該函數,如果不存在的話創建一個立即執行函數,該函數將創建一個assign函數綁定到Object上。
- 判斷參數是否正確(目的對象不能為空,我們可以直接設置{}傳遞進去,但必須設置該值)
- 使用Object在原有的對象基礎上返回該對象,並保存為out
- 使用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