instanceof是如何在JavaScript中实现的

How instanceof is implemented in JavaScript

本文关键字:实现 JavaScript instanceof      更新时间:2023-09-26

让我们考虑以下代码片段:

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实例的求值如下:

  1. 设lref是计算RelationalExpression的结果
  2. 设lval为GetValue(lref)
  3. 设rref是计算ShiftExpression的结果
  4. 设rval为GetValue(rref)
  5. 如果Type(rval)不是Object,则引发TypeError异常
  6. 如果rval没有[[HasInstance]]内部方法,则引发TypeError异常
  7. 返回使用参数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.prototypeB.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>