Object.assign()從認識到實現

海寧不想說話發表於2019-03-06

1.這是個什麼:

Object.assign() 方法用於將所有可列舉屬性的值從一個或多個源物件複製到目標物件。它將返回目標物件。

MDN這樣說。

    • let newObj=Object.assign(target,...source)//target為目標物件    source為源物件
  • 常見特性:
    1. 只能夠複製源物件中可列舉的屬性,繼承得以及enumerable:false的屬性都不能被拷貝。
    2. 對於目標物件中已經存在的屬性,源物件將會根據在arguments中的順序進行依次覆蓋。
    3. 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' } }複製程式碼

    4. 目標物件不能為nullundefined,這裡可以看看Object.assign()的對於targetnullundefined時的原生表現。

      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()

  • 思路:
    1. 先對target物件進行檢測是否為null || undefined,是的話丟擲TypeError
    2. Object.defineProperty定義assign2屬性。
    3. Object()target包裝為一個物件to
    4. arguments中的源物件依次進行屬性遍歷(for...of...)用Object.hasOwnProperty進行屬性檢查,有則進行覆蓋,沒有則建立屬性進行賦值(址)。
    5. 返回新物件to
  • 知識儲備:
Object.defineProperty() 方法會直接在一個物件上定義一個新屬性,或者修改一個物件的現有屬性, 並返回這個物件。
  1. 這個方法會有個預設行為需注意:

    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))    //[]複製程式碼

    • 很明顯看到Objectassign方法是不能被遍歷到的,然而如果我們直接用下面這種方式定義,我們再看一下結果。

      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對於只讀屬性修改時只會靜默失敗,不會報錯,在嚴格模式下報錯,所以我們這裡開啟嚴格模式。
      • 前面說過targetnull || 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()

    方法使用現有物件作為新建立物件的原型來建立新物件



    相關文章