今天我碰巧有太多时间可以杀死,我在Node(v0.10.13)命令行中玩了一下:
> 1 instanceof Object
false
> (1).__proto__
{}
> (1).__proto__ instanceof Object
true
> (1).__proto__.__proto__ === Object.prototype
true
现在,根据MDN,instanceof
的作用是:
instanceof运算符测试对象是否在其原型中 链接构造函数的prototype属性。
但显然Object.prototype
位于1
的原型链中。那么为什么1 instanceof Object
是假的呢?也许是因为1
是一个原始而不是一个开头的对象?
好的,我接受了,我做了更多测试:
> (1).__proto__ === (2).__proto__
true
> 'a'.__proto__ === 'b'.__proto__
true
> (1).__proto__ === 'a'.__proto__
false
> (1).__proto__.__proto__ === 'a'.__proto__.__proto__
true
> (1).__proto__.type = 'number'
'number'
> 'a'.__proto__.type = 'string'
'string'
> (2).type
'number'
> (1.5).type
'number'
> 'b'.type
'string'
显然,所有数字基元都从一个对象继承,并且所有字符串基元都从另一个对象继承。这两个对象都继承自Object.prototype
。
现在问题是,如果数字和字符串被认为是原语,为什么要从其他对象继承它们?或者相反,当它们从其他对象继承时,为什么不考虑它们呢?对我来说,对象的孩子不是一个对象似乎是荒谬的。
顺便说一下,我也在Firefox 22中对它们进行了测试,得到了相同的结果。
答案 0 :(得分:29)
你被一种通常被称为“拳击”(c# related article,java related article)的机制所欺骗,这种机制会使所有遇到它的人陷入困境。你在开始时得到了正确答案:
也许因为1是原始而不是开始的对象?
确实如此。但是,原语如何能够包含方法?它们如何包含属性?毕竟,在js中,它们以尽可能低的水平表示(见#4.3.2)。为了使这些值真正有用,无论何时执行primitive.property
,都会发生以下情况(#11.2.1):
Object(primitive).property;
换句话说,js有自动装箱。这可以用我最喜欢的技巧证明:
var primitive = 'food';
primitive.isPizza = true; //yummy
console.log(primitive.isPizza); //undefined. where did my pizza go!?
由于这拳击, primitive.isPizza
消失了:
var primitive = 'food';
Object(primitive).isPizza = true;
console.log(Object(primitive).isPizza);
盒装primitive
是它自己独特的雪花 - 当你第二次装盒时,它并不是指同一件事。盒装值很快就会被GCd所遗忘,并在时间的迷雾中被遗忘。
如果你的原语不是原语,那就不会发生这种情况:
var obj = new String('food');
obj.isPizza = true;
console.log(obj.isPizza); //true
这是否意味着你应该只使用对象,而不是原语?不,原因很简单,你做的时间需要在元素上存储元数据是非常少而且对象复杂化了:
obj === primitive; //false, obj is an object, primitive is a primitive