Object.assign函數(shù)的使用,使用該函數(shù)我們可以快速的復(fù)制一個(gè)或者多個(gè)對(duì)象到目標(biāo)對(duì)象中,本文內(nèi)容涉及es6,es7相關(guān)的對(duì)象復(fù)制的內(nèi)容骂租,以及一些es5的替代方案的介紹。
函數(shù)原型
首先看一下函數(shù)的定義:
函數(shù)參數(shù)為一個(gè)目標(biāo)對(duì)象(該對(duì)象作為最終的返回值),源對(duì)象(此處可以為任意多個(gè))斑司。通過調(diào)用該函數(shù)可以拷貝所有可被枚舉的自有屬性值到目標(biāo)對(duì)象中。
Object.assign(target, ...sources)
這里我們需要強(qiáng)調(diào)的三點(diǎn)是:
可被枚舉的屬性
自有屬性
string或者Symbol類型是可以被直接分配的
拷貝過程中將調(diào)用源對(duì)象的getter方法,并在target對(duì)象上使用setter方法實(shí)現(xiàn)目標(biāo)對(duì)象的拷貝宿刮。
函數(shù)實(shí)例
這里我們通過幾個(gè)MDN上的例子來介紹一下使用方法:
實(shí)例一
我們參考上面的原型函數(shù)說明即可知道其最開始的o1因?yàn)樵O(shè)置為target互站,則調(diào)用其setter方法設(shè)置了其他對(duì)象的屬性到自身。
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.
實(shí)例二
我們自定義了一些對(duì)象僵缺,這些對(duì)象有一些包含了不可枚舉的屬性,另外注意使用 Object.defineProperty 初始化的對(duì)象默認(rèn)是不可枚舉的屬性胡桃。對(duì)于可枚舉的對(duì)象我們可以直接使用Object.keys()獲得,或者使用for-in循環(huán)遍歷出來.
對(duì)于不可枚舉的屬性,使用Object.assign的時(shí)候?qū)⒈蛔詣?dòng)忽略磕潮。
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 }
實(shí)例三
對(duì)于只讀的屬性翠胰,當(dāng)分配新的對(duì)象覆蓋他的時(shí)候,將拋出異常:
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
這里我們簡(jiǎn)單的看下如何實(shí)現(xiàn)es5版本的Object.assign:
實(shí)現(xiàn)步驟:
判斷是否原生支持該函數(shù)自脯,如果不存在的話創(chuàng)建一個(gè)立即執(zhí)行函數(shù)之景,該函數(shù)將創(chuàng)建一個(gè)assign函數(shù)綁定到Object上。
判斷參數(shù)是否正確(目的對(duì)象不能為空膏潮,我們可以直接設(shè)置{}傳遞進(jìn)去,但必須設(shè)置該值)
使用Object在原有的對(duì)象基礎(chǔ)上返回該對(duì)象锻狗,并保存為out
使用for…in循環(huán)遍歷出所有的可枚舉的自有對(duì)象。并復(fù)制給新的目標(biāo)對(duì)象(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;
};
})();
}
擴(kuò)展內(nèi)容
1.深度復(fù)制
當(dāng)我們調(diào)用下面的函數(shù)的時(shí)候焕参,由于Object.assign將覆蓋之前的內(nèi)容轻纪,所以并不能完全的做到融合對(duì)象,而是全部替換掉叠纷,所以返回的對(duì)象內(nèi)容將變成最后一個(gè)值;{a: {c: 3} Object.assign({a: {b: 0}}, {a: {b: 1, c: 2}}, {a: {c: 3}});
如何深層次的融合對(duì)象刻帚,比如我們期望的輸出結(jié)果為:
{a:{b:1,c:3}}
這樣我們必須實(shí)現(xiàn)自己的算法來完成深層復(fù)制了,不過github上已經(jīng)有很多好的解決方案,比如deep-merge 通過遞歸的方式逐層的去調(diào)用assign函數(shù)涩嚣。
2.ES2016實(shí)現(xiàn)
在es7中我們使用rest屬性可以捕獲所有剩余的對(duì)象內(nèi)容比如下面的例子(可使用babel-repl頁(yè)面測(cè)試崇众,瀏覽器一般尚未支持):
let { fname, lname, ...rest } = { fname: "Hemanth", lname: "HM", location: "Earth", type: "Human" };
fname; //"Hemanth"
lname; //"HM"
rest; // {location: "Earth", type: "Human"}
這樣我們就可以使用該特性來實(shí)現(xiàn)assign函數(shù)
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"}
不過仍舊只是淺層的替換,并沒有實(shí)現(xiàn)深層次的合并缓艳。
3.注意
如果目標(biāo)對(duì)象與源對(duì)象有同名屬性校摩,則后面的屬性會(huì)覆蓋前面的屬性
如果只有一個(gè)參數(shù),則直接返回該參數(shù)阶淘。即Object.assign(obj) === obj
如果第一個(gè)參數(shù)不是對(duì)象衙吩,而是基本數(shù)據(jù)類型(Null、Undefined除外)溪窒,則會(huì)調(diào)用對(duì)應(yīng)的基本包裝類型
如果第一個(gè)參數(shù)是Null和Undefined坤塞,則會(huì)報(bào)錯(cuò);如果Null和Undefined不是位于第一個(gè)參數(shù)澈蚌,則會(huì)略過該參數(shù)的復(fù)制