在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)
吧!其余时候,请用{}
。
以上