让我们考虑以下代码片段:
function A() {}
var obj = new A();
function B() {};
obj.constructor = B;
console.info("1: ", obj.constructor); //Function B
console.info("2: ", obj instanceof A); //true
console.info("3: ", obj instanceof B); //false
我想,要决定该对象是否是某个函数类的实例,JS引擎必须检查该对象是否具有相同的构造函数属性。但这似乎并没有发生,因为即使重写对象的构造函数属性也不会改变它的输出实例。
ECMAScript 262规范将告诉您这一点。直接从其引用`instanceof operator部分:
11.8.6运算符的实例
ShiftExpression的生产RelativalExpression:RelativalExpression实例的求值如下:
- 设lref是计算RelationalExpression的结果
- 设lval为GetValue(lref)
- 设rref是计算ShiftExpression的结果
- 设rval为GetValue(rref)
- 如果Type(rval)不是Object,则引发TypeError异常
- 如果rval没有[[HasInstance]]内部方法,则引发TypeError异常
- 返回使用参数lval调用rval的[[HasInstance]]内部方法的结果
至于[[HasInstance]]
是什么,规范还在对象内部属性和方法部分中定义为:
[[HasInstance]]
返回一个布尔值,指示参数是否可能是由该对象构造的Object。在标准的内置ECMAScript对象中,只有Function对象实现[[HasInstance]]
对此的进一步解读:http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.4.5.3
此链接声明:
instanceof运算符测试中constructor.prototype的存在对象的原型链。
简短版本
obj instanceof A
查看A.prototype
引用的对象是否在obj
的原型链中的任何位置。它根本不使用constructor
。
更多详细信息
这在规范中由§11.8.5-运算符的实例涵盖,该实例(通过§8.6.2间接地)表示,它调用函数对象的[[HasInstance]]
内部方法,传入我们正在测试的对象。Function
的[[HasInstance]]
(在§15.3.5.3中)表示,它从函数的prototype
属性中获取对象引用,然后如果该对象位于目标对象原型链中的任何位置,则返回true
,如果不在,则返回false
。
它不使用constructor
(事实上,JavaScript本身没有任何功能)—如果你仔细想想,它不能,因为一个对象的constructor
属性只能指向一个函数,但一个对象可以是instanceof
多个函数—例如,在伪经典继承的情况下:
function A() {}
function B() {
A.call(this);
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
var obj = new B();
snippet.log(obj instanceof A); // true
snippet.log(obj instanceof B); // true
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
两者都是真的,因为A.prototype
和B.prototype
引用的两个对象都在obj
的原型链中。
instanceof
为真并不一定意味着obj
是由对A
的调用直接或间接创建的;它只是表明它们之间有一个模糊的链接(A.prototype
指的是一个也在obj
的原型链中的对象)。它通常意味着A
参与了创建对象,但不能保证。
例如:
function A() {}
var obj = Object.create(A.prototype);
snippet.log(obj instanceof A); // true
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
请注意,根本没有调用A
来创建对象。
或者可能更清晰和/或更引人注目:
function A() {}
var p = {};
var obj = Object.create(p);
snippet.log(obj instanceof A); // false
A.prototype = p;
snippet.log(obj instanceof A); // true
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
还有一个不寻常但完全可能的版本:
function A() {}
function B() {}
A.prototype = B.prototype = {};
var obj = new A();
snippet.log(obj instanceof B); // true
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
或者这个:
function A() {}
function B() {}
var obj = new B();
snippet.log(obj instanceof A); // false
A.prototype = B.prototype;
snippet.log(obj instanceof A); // true
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>