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