詳解Object.create(null)

司想君發表於2018-04-11

在Vue和Vuex的原始碼中,作者都使用了Object.create(null)來初始化一個新物件。為什麼不用更簡潔的{
}
呢?

SegmentFaultStack Overflow等開發者社群中也有很多人展開了討論,在這裡總結成文,溫故知新。

Object.create()的定義

照搬一下MDN上的定義:

Object.create(proto,[propertiesObject])複製程式碼
  • proto:新建立物件的原型物件
  • propertiesObject:可選。要新增到新物件的可列舉(新新增的屬性是其自身的屬性,而不是其原型鏈上的屬性)的屬性。

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

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控制檯列印如下:

詳解Object.create(null)

從上圖可以看到,新建立的物件繼承了Object自身的方法,如hasOwnPropertytoString等,在新物件上可以直接使用。

再看看使用Object.create()建立物件:

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

在chrome控制檯列印如下:

詳解Object.create(null)

可以看到,新建立的物件除了自身屬性a之外,原型鏈上沒有任何屬性,也就是沒有繼承Object的任何東西,此時如果我們呼叫o.toString()會報Uncaught TypeError的錯誤。

大家可能會注意到,第一個引數使用了null。也就是說將null設定成了新建立物件的原型,自然就不會有原型鏈上的屬性。我們再把上面的例子改一改:

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

null改為{
}
,結果是怎樣的?在chrome控制檯列印如下:

詳解Object.create(null)

我們看到,這樣建立的物件和使用{
}
建立物件已經很相近了,但是還是有一點區別:多了一層proto巢狀。

我們最後再來改一下:

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

chrome控制檯列印如下:

詳解Object.create(null)

這次就和使用{
}
建立的物件一模一樣了。至此,我相信大家已經對兩者的區別十分清楚了。

Object.create(null)的使用場景

再回到文章開頭的問題,為什麼很多原始碼作者會使用Object.create(null)來初始化一個新物件呢?這是作者的習慣,還是一個最佳實踐?

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

我們進一步比較一下Object.create(null){
}
建立控物件的區別:

在chrome列印如下:

詳解Object.create(null)

從上圖可以看到,使用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)吧!其他時候,請用{
}

以上

來源:https://juejin.im/post/5acd8ced6fb9a028d444ee4e

相關文章