Object.create
在Vue和Vuex的源码中,作者都使用了Object.create(null)来初始化一个新对象。为什么不用更简洁的{}呢?
在SegmentFault和Stack Overflow等开发者社区中也有很多人展开了讨论,在这里总结成文,温故知新。
定义
照搬一下MDN上的定义:
1  | Object.create(proto,[propertiesObject])  | 
- proto:新创建对象的原型对象
 - propertiesObject:可选。要添加到新对象的可枚举(新添加的属性是其自身的属性,而不是其原型链上的属性)的属性。
 
实现类式继承
下面的例子演示了如何使用Object.create()来实现类式继承。这是一个所有版本JavaScript都支持的单继承。
1  | // Shape - 父类(superclass)  | 
如果你希望能继承到多个对象,则可以使用混入的方式。
1  | function MyClass() {  | 
Object.assign 会把OtherSuperClass原型上的函数拷贝到 MyClass原型上,使 MyClass 的所有实例都可用 OtherSuperClass 的方法。Object.assign 是在 ES2015 引入的,且可用 polyfilled。要支持旧浏览器的话,可用使用 jQuery.extend() 或者 _.assign()。
使用 Object.create 的 propertyObject参数
1  | var o;  | 
Object.create()、{…}的区别
先看看我们经常使用的{}创建的对象是什么样子的:
1  | var o = {a: 1};  | 
在chrome控制台打印如下:

从上图可以看到,新创建的对象继承了Object自身的方法,如hasOwnProperty、toString等,在新对象上可以直接使用。
再看看使用Object.create()创建对象:
1  | var o = Object.create(null,{  | 
在chrome控制台打印如下:

可以看到,新创建的对象除了自身属性a之外,原型链上没有任何属性,也就是没有继承Object的任何东西,此时如果我们调用o.toString()会报Uncaught TypeError的错误。
大家可能会注意到,第一个参数使用了null。也就是说将null设置成了新创建对象的原型,自然就不会有原型链上的属性。我们再把上面的例子改一改:
1  | var o = Object.create({},{  | 
将null改为{},结果是怎样的?在chrome控制台打印如下:

我们看到,这样创建的对象和使用{}创建对象已经很相近了,但是还是有一点区别:多了一层proto嵌套。
我们最后再来改一下:
1  | var o = Object.create(Object.prototype,{  | 
chrome控制台打印如下:

这次就和使用{}创建的对象一模一样了。至此,我相信大家已经对两者的区别十分清楚了。
Object.create(null)的使用场景
再回到文章开头的问题,为什么很多源码作者会使用Object.create(null)来初始化一个新对象呢?这是作者的习惯,还是一个最佳实践?
其实都不是,这并不是作者不经思考随便用的,也不是javascript编程中的最佳实践,而是需要因地制宜,具体问题具体分析。
我们进一步比较一下Object.create(null)和{}创建控对象的区别:
在chrome打印如下:

从上图可以看到,使用create创建的对象,没有任何属性,显示No properties,我们可以把它当作一个非常纯净的map来使用,我们可以自己定义hasOwnProperty、toString方法,不管是有意还是不小心,我们完全不必担心会将原型链上的同名方法覆盖掉。举个例子:
1  | //Demo1:  | 
另一个使用create(null)的理由是,在我们使用for..in循环的时候会遍历对象原型链上的属性,使用create(null)就不必再对属性进行检查了,当然,我们也可以直接使用Object.keys[]。
总结
- 你需要一个非常干净且高度可定制的对象当作数据字典的时候;
 - 想节省
hasOwnProperty带来的一丢丢性能损失并且可以偷懒少些一点代码的时候 
用Object.create(null)吧!其他时候,请用{}。