昨天我们的分享会聊到ES6的collections部分,回头我看了下es6-in-depth,里面有这么一句话:“纯粹的对象不可遍历,也就是,它们不能配合for-of
循环或...操作符等语法。”
阿咧?!纯粹的对象是什么鬼。。。
传统的JS对象都是不纯粹的
var obj = {}
,你觉得obj是个纯粹的对象嘛?答案是否定的。
控制台里敲上上线的语句,然后看看obj是什么:
你可能觉得obj = {}
,它就什么都没有了,但实际上{}
这个东西的声明方式是通过原型链继承了Object
,也就生来自带了Object
的一系列内置属性和方法,不信你可以继续验证:
所以说,
var obj = {}
相当于是执行了这个语句 var obj = Object.create(Object.prototype)
:
那么到这里为止,我们就可以知道var obj = {}
声明得到的obj,它一点儿都不纯粹。然而悲催的是,虽然它生来自带了一串东西,但是这些东西不是enumerable
的,所以又不能被遍历出来。用obj.propertyIsEnumerable()
来判断,都是false
;用for...in
遍历,毛线都没有。
怎么才能纯粹?
答案是Object.create(null)
,我们可以先验证一下:
没有那一串内置属性和方法了,干干净净的。而且完全不是继承Object得来的:
为什么是纯粹的?
那就要看下Object.create()是怎么实现的了... 这里是一段Object.create()的polyfill:
if (typeof Object.create != 'function') {
// Production steps of ECMA-262, Edition 5, 15.2.3.5
// Reference: http://es5.github.io/#x15.2.3.5
Object.create = (function() {
//为了节省内存,使用一个共享的构造器
function Temp() {}
// 使用 Object.prototype.hasOwnProperty 更安全的引用
var hasOwn = Object.prototype.hasOwnProperty;
return function (O) {
// 1. 如果 O 不是 Object 或 null,抛出一个 TypeError 异常。
if (typeof O != 'object') {
throw TypeError('Object prototype may only be an Object or null');
}
// 2. 使创建的一个新的对象为 obj ,就和通过
// new Object() 表达式创建一个新对象一样,
// Object是标准内置的构造器名
// 3. 设置 obj 的内部属性 [[Prototype]] 为 O。
Temp.prototype = O;
var obj = new Temp();
Temp.prototype = null; // 不要保持一个 O 的杂散引用(a stray reference)...
// 4. 如果存在参数 Properties ,而不是 undefined ,
// 那么就把参数的自身属性添加到 obj 上,就像调用
// 携带obj ,Properties两个参数的标准内置函数
// Object.defineProperties() 一样。
if (arguments.length > 1) {
// Object.defineProperties does ToObject on its first argument.
var Properties = Object(arguments[1]);
for (var prop in Properties) {
if (hasOwn.call(Properties, prop)) {
obj[prop] = Properties[prop];
}
}
}
// 5. 返回 obj
return obj;
};
})();
}
从代码里很明显能看到create的操作过程:
判断,然后把O本身的属性(并没有Object里面内置的属性)一个个赋值给用临时构造器Temp生成的obj上,作为obj的属性。所以如果O是null的话,O没有属性,obj也就什么都没有。
纯粹了又能怎样
噢那纯粹了的话好处可多了。
如果拿不纯粹的空对象做计算的话,totally懵逼的好嘛!
({} + 1).length === 16 //true !!
当然啦,如果你弄得清楚是因为
Object.valueOf()
和Object.toString()
在背后搞的鬼也算还好,但这种东西看到了真的只想说“扎心了老铁”。
那如果拿纯粹的对象做计算的话,sorry,I'm pure and I do not have the 'valueOf' and the 'toString' to help me calculating ...
用一句别人的话来说“「纯粹」的对象适合用于存储键值对数据,而且没有隐式的类型转换,更加直观。”
当然了,除了继承了Object.prototype
这点,Object.create(null)
和 {}
之间没有别的区别了,在操作对象这一点上完全一致,存取的性能没差啦。但如果并不care自己的空对象继承了Object
的话,宝宝们还是使用{}
来声明吧,因为在创建性能上{}
比Object.create(null)
快了20倍呢!!!