1.這是個什麼:
Object.assign()
方法用於將所有可列舉屬性的值從一個或多個源物件複製到目標物件。它將返回目標物件。
MDN
這樣說。
let newObj=Object.assign(target,...source)//target為目標物件 source為源物件
- 常見特性:
- 只能夠複製源物件中可列舉的屬性,繼承得以及
enumerable:false
的屬性都不能被拷貝。 - 對於目標物件中已經存在的屬性,源物件將會根據在
arguments
中的順序進行依次覆蓋。 assign
為淺拷貝,基本型別進行值拷貝,引用型別進行址拷貝。let a={ name:132, age:44, previous:{ limit:'old' }} let b=Object.assign({},a); console.log(b); //{ name: 132, age: 44, previous: { limit: 'old' } } a.name='new name'; a.previous.limit='new'; console.log(b); //{ name: 132, age: 44, previous: { limit: 'new' } }複製程式碼
- 目標物件不能為
null
和undefined
,這裡可以看看Object.assign()
的對於target
為null
和undefined
時的原生表現。let b={ name:132, age:44, previous:43242} let c={ previous:'dsfs0' } let d=Object.assign(null,b,c); //Cannot convert undefined or null to object複製程式碼
2.實現一個Object.assign()
- 思路:
- 先對
target
物件進行檢測是否為null || undefined
,是的話丟擲TypeError
。 - 用
Object.defineProperty
定義assign2
屬性。 - 用
Object()
將target
包裝為一個物件to
。 - 對
arguments
中的源物件依次進行屬性遍歷(for...of...
)用Object.hasOwnProperty
進行屬性檢查,有則進行覆蓋,沒有則建立屬性進行賦值(址)。 - 返回新物件
to
。
- 知識儲備:
Object.defineProperty()
方法會直接在一個物件上定義一個新屬性,或者修改一個物件的現有屬性, 並返回這個物件。
- 這個方法會有個預設行為需注意:
Object.defineProperty(target,key,{ // enumerable:false, // writable:false, // configurable:false, })複製程式碼
這對我們還原assign
原生行為很重要。
Object建構函式建立一個物件包裝器。
hasOwnProperty()
方法會返回一個布林值,指示物件自身屬性中是否具有指定的屬性。
該方法會判斷屬性是否屬於物件例項,來自原型和繼承得的屬性將不會被遍歷。
- 具體實現(直接上程式碼了)
if(typeof Object.assign2 !=='function'){ Object.defineProperty(Object,"assign2",{ value(target){ 'use strict' if(target==null){ //underfined null 兩種非法情況 throw new TypeError("danger type error"); } let to=Object(target); for(let index=1;index<arguments.length;index++){ let nextSouce=arguments[index]; if(nextSouce!==null){ for(let nextKey in nextSouce){ if(!Object.prototype.hasOwnProperty.call(to,nextKey)){ to[nextKey]=nextSouce[nextKey]; }else{ if(to[nextKey]!==nextSouce[nextKey]){ to[nextKey]=nextSouce[nextKey]; } } } } } return to; }, writable:true, configurable:true })} 複製程式碼
- 要注意什麼...
- 為什麼使用
Object.defineProperty
來定義assign2
屬性。
- 先來看一段程式碼
console.log(Object.keys(Object)) //[]複製程式碼
- 很明顯看到
Object
的assign
方法是不能被遍歷到的,然而如果我們直接用下面這種方式定義,我們再看一下結果。Object.assign2=function(){}console.log(Object.getOwnPropertyDescriptor(Object,'assign2'))/* { value: [Function], writable: true, enumerable: true, //注意這裡 configurable: true } */ //而對於原生assign console.log(Object.getOwnPropertyDescriptor(Object,'assign'))/* { value: [Function], writable: true, enumerable: false, configurable: true } */複製程式碼
- 根據前面說的
Object.defineProperty
的預設行為,這裡使用它就顯得很合理了。 - 為什麼使用嚴格模式
'use strict'
- 我們先來看一個東西
let obj=Object('123');console.log(Object.getOwnPropertyDescriptor(obj,'0'))// { value: '1',// writable: false, //注意這裡只讀// enumerable: true,// configurable: false // }複製程式碼
- 對於只讀的屬性,
Object.assign()
對於只讀屬性的處理情況是let obj=Object.defineProperty({},'0',{ value:'123',})let newObj=Object.assign(obj,{0:456});console.log(newObj) //報錯 TypeError: Cannot assign to read only property '0' of object '#<Object>'複製程式碼
- 而原生js對於只讀屬性修改時只會靜默失敗,不會報錯,在嚴格模式下報錯,所以我們這裡開啟嚴格模式。
- 前面說過
target
為null || undefined
時會報錯。所以我們應該對這兩種情況進行檢查,為什麼我們只判斷了是否等於null呢,看程式碼console.log(null==undefined) //trueconsole.log(null === undefined) //false複製程式碼
- 看到這裡應該理解了。
- 為什麼我們用
Object.prototype.hasOwnProperty.call
來檢測屬性呢,來看一種情況let obj=Object.create(null);console.log(Object.getOwnPropertyDescriptor(obj,'assign'))複製程式碼
- 當我們以
null
為原型建立新物件時,此時建立的物件是沒有指向原型鏈的,所以定義在Object.prototype
上的hasOwnProperty
方法就不能被訪問到.
Object.create()
方法使用現有物件作為新建立物件的原型來建立新物件