在Vue和Vuex的原始碼中,作者都使用了
Object.create(null)
來初始化一個新物件。為什麼不用更簡潔的{
呢?
}在
SegmentFault
和Stack 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
自身的方法,如hasOwnProperty
、toString
等,在新物件上可以直接使用。
再看看使用Object.create()
建立物件:
var o = Object.create(null,{
a:{
writable:true, configurable:true, value:'1'
}
})console.log(o)複製程式碼
在chrome控制檯列印如下:
可以看到,新建立的物件除了自身屬性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來使用,我們可以自己定義hasOwnProperty
、toString
方法,不管是有意還是不小心,我們完全不必擔心會將原型鏈上的同名方法覆蓋掉。舉個例子:
//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[]
。
總結:
- 你需要一個非常乾淨且高度可定製的物件當作資料字典的時候;
- 想節省
hasOwnProperty
帶來的一丟丟效能損失並且可以偷懶少些一點程式碼的時候
用Object.create(null)
吧!其他時候,請用{
。
}
以上