原文:http://www.2ality.com/2012/08/instanceof-object.html
问题是: 什么对象不是Object的实例?换句话说就是:变量v是什么样的值,可以让下面的三个表达式都为true?
typeof v === "object"
v !== null
!(v instanceof Object)
1.关于v,我们知道些什么?
上面的表达式告诉我们关于v的两件事:它是一个对象,但他不是Object的实例.
1.1 v是一个对象
前两个表达式使用typeof操作符来确保v是一个对象.第二个表达式是必须的,因为typeof有个bug,那就是在操作原始值null时,返回"object".这样一来,v就是一个完完全全的对象了.
记得JavaScript中只有两种类型的值吗:原始值和对象值.原始值是只读的(不可变的),也就是说:你不能尝试在它身上创建或者修改属性值,这样的操作不会有任何效果:
> var str = "abc";
> str.foo = 123;
123
> str.foo
undefined
> str.length
3
> str.length = 1;
> str.length
3
原始值的比较是通过值来比较的,也就是说:一个值的内容决定了它是否和另外一个相同类型的值相等:
不同的是,对象值是可变值,对象值之间是通过引用来比较的.每个对象值都有自己唯一的标识符.即便它们有相同的内容,两个对象也只有在拥有相同的标识符时才算相等:
> new String("abc") === new String("abc")
false
> {} === {}
false
> var obj = {};
> obj === obj
true
1.2 v不是Object的实例
第三个表达式告诉我们,v不是Object的实例.但这又意味着什么?在执行下面的表达式时
instanceof操作符会检查type.prototype是否在value的原型链上.也就是说,如果我们自己实现一个instanceof函数,完全可以这么写(不考虑一些错误的检查,比如type为null等):
function myInstanceof(value, type) {
return type.prototype.isPrototypeOf(value);
}
2.寻找一个没有原型的对象
因此,我们必须找到一个自身的原型链中没有Object.prototype的对象.没有一个内置的类型符合这个要求:
> Object.prototype.isPrototypeOf({})
true
> Object.prototype.isPrototypeOf([])
true
> Object.prototype.isPrototypeOf(/abc/)
true
不过,我们可以使用Object.create来创建一个没有任何原型的的对象,甚至也不继承Object.prototype:
> var obj = Object.create(null)
undefined
> Object.getPrototypeOf(obj)
null
> Object.prototype.isPrototypeOf(obj)
false
obj没有任何标准的方法,比如toString()方法,但它仍然是一个对象,可以有属性和其他所有的对象特性.这样就发现了我们要找的v:
> obj instanceof Object
false
> typeof obj
'object'
另一个答案. 鉴于原型链必然有尽头,而这个尽头就是Object.prototype,该对象没有自己的原型,下面的代码证明了这一点:
> Object.prototype instanceof Object
false
> typeof Object.prototype
'object'
其他内置类型的prototype属性都是有自己的原型的,比如Date:
> Object.getPrototypeOf(Date.prototype)
{}
译者注:我只答出了第二个答案.
3.参考
- JavaScript values: not everything is an object
- Improving the JavaScript typeof operator