在Vue和Vuex的源碼中,做者都使用了Object.create(null)
來初始化一個新對象。爲何不用更簡潔的{}
呢?javascript在
SegmentFault
和Stack Overflow
等開發者社區中也有不少人展開了討論,在這裏總結成文,溫故知新。java
照搬一下MDN上的定義:chrome
Object.create(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()
的定義其實很簡單,弄清楚上面這個例子就能夠了。bash
先看看咱們常用的{}
建立的對象是什麼樣子的:性能
var o = {a:1};
console.log(o)
複製代碼
在chrome控制檯打印以下:ui
從上圖能夠看到,新建立的對象繼承了Object
自身的方法,如hasOwnProperty
、toString
等,在新對象上能夠直接使用。this
再看看使用Object.create()
建立對象:spa
var o = Object.create(null,{
a:{
writable:true,
configurable:true,
value:'1'
}
})
console.log(o)
複製代碼
在chrome控制檯打印以下:prototype
能夠看到,新建立的對象除了自身屬性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)
來初始化一個新對象呢?這是做者的習慣,仍是一個最佳實踐?
其實都不是,這並非做者不經思考隨便用的,也不是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)
吧!其餘時候,請用{}
。
以上