詳解Object.create(null)

在Vue和Vuex的源碼中,做者都使用了 Object.create(null)來初始化一個新對象。爲何不用更簡潔的 {}呢?

SegmentFaultStack Overflow等開發者社區中也有不少人展開了討論,在這裏總結成文,溫故知新。javascript

Object.create()的定義

照搬一下MDN上的定義:java

Object.create(proto,[propertiesObject])
  • proto:新建立對象的原型對象
  • propertiesObject:可選。要添加到新對象的可枚舉(新添加的屬性是其自身的屬性,而不是其原型鏈上的屬性)的屬性。

舉個例子(惡改了一下MDN的官方例子,看懂的點贊):chrome

const car = {
  isSportsCar: false,
  introduction: function () {
    console.log(`Hi girl, this is a ${this.name}. Do you like to have a drink with me ? ${this.isSportsCar}`);
  }
};

const porsche = Object.create(car,{
    //color成爲porsche的數據屬性
    //顏色不喜歡,能夠改色或貼膜,因此可修改
    color:{
        writable:true,
        configurable:true,
        value:'yellow'
    },
    //type成爲porsche的訪問器屬性
    type:{
        // writable、configurable等屬性,不顯式設置則默認爲false
        // 想把普通車改爲敞篷,成本有點大了,因此就設成不可配置吧
        get:function(){return 'convertible'},
        set:function(value){"change this car to",value}
    }
});

porsche.name = "Porsche 911"; // "name"是"porsche"的屬性, 而不是"car"的
porsche.isSportsCar = true; // 繼承的屬性能夠被覆寫

porsche.introduction();
// expected output: "Hi girl, this is a Porsche 911. Do you like to have a drink with me ? true"

Object.create()的定義其實很簡單,弄清楚上面這個例子就能夠了。編程

Object.create()、{...}的區別

先看看咱們常用的{}建立的對象是什麼樣子的:性能

var o = {a:1};
console.log(o)

在chrome控制檯打印以下:this

圖片描述

從上圖能夠看到,新建立的對象繼承了Object自身的方法,如hasOwnPropertytoString等,在新對象上能夠直接使用。spa

再看看使用Object.create()建立對象:prototype

var o = Object.create(null,{
    a:{
           writable:true,
        configurable:true,
        value:'1'
    }
})
console.log(o)

在chrome控制檯打印以下:code

圖片描述

能夠看到,新建立的對象除了自身屬性a以外,原型鏈上沒有任何屬性,也就是沒有繼承Object的任何東西,此時若是咱們調用o.toString()會報Uncaught TypeError的錯誤。對象

你們可能會注意到,第一個參數使用了null。也就是說將null設置成了新建立對象的原型,天然就不會有原型鏈上的屬性。咱們再把上面的例子改一改:

var o = Object.create({},{
    a:{
           writable:true,
        configurable:true,
        value:'1'
    }
})
console.log(o)

null改成{},結果是怎樣的?在chrome控制檯打印以下:

圖片描述

咱們看到,這樣建立的對象和使用{}建立對象已經很相近了,可是仍是有一點區別:多了一層proto嵌套。

咱們最後再來改一下:

var o = Object.create(Object.prototype,{
    a:{
           writable:true,
        configurable:true,
        value:'1'
    }
})
console.log(o)

chrome控制檯打印以下:

圖片描述

此次就和使用{}建立的對象如出一轍了。至此,我相信你們已經對二者的區別十分清楚了。

Object.create(null)的使用場景

再回到文章開頭的問題,爲何不少源碼做者會使用Object.create(null)來初始化一個新對象呢?這是做者的習慣,仍是一個最佳實踐?

其實都不是,這並非做者不經思考隨便用的,也不是javascript編程中的最佳實踐,而是須要因地制宜,具體問題具體分析。

咱們進一步比較一下Object.create(null){}建立控對象的區別:

在chrome打印以下:

圖片描述

從上圖能夠看到,使用create建立的對象,沒有任何屬性,顯示No properties,咱們能夠把它看成一個很是純淨的map來使用,咱們能夠本身定義hasOwnPropertytoString方法,不論是有意仍是不當心,咱們徹底沒必要擔憂會將原型鏈上的同名方法覆蓋掉。舉個例子:

//Demo1:
var a= {...省略不少屬性和方法...};
//若是想要檢查a是否存在一個名爲toString的屬性,你必須像下面這樣進行檢查:
if(Object.prototype.hasOwnProperty.call(a,'toString')){
    ...
}
//爲何不能直接用a.hasOwnProperty('toString')?由於你可能給a添加了一個自定義的hasOwnProperty
//你沒法使用下面這種方式來進行判斷,由於原型上的toString方法是存在的:
if(a.toString){}

//Demo2:
var a=Object.create(null)
//你能夠直接使用下面這種方式判斷,由於存在的屬性,都將定義在a上面,除非手動指定原型:
if(a.toString){}

另外一個使用create(null)的理由是,在咱們使用for..in循環的時候會遍歷對象原型鏈上的屬性,使用create(null)就沒必要再對屬性進行檢查了,固然,咱們也能夠直接使用Object.keys[]

總結一下:

  1. 你須要一個很是乾淨且高度可定製的對象看成數據字典的時候;
  2. 想節省hasOwnProperty帶來的一丟丟性能損失而且能夠偷懶少些一點代碼的時候

Object.create(null)吧!

其餘時候,請用{}

以上

相關文章
相關標籤/搜索