为什么在 JavaScript 中同时执行Object instanceof Function和Function instanceof Object返回true?
我在 Safari WebInspector 中尝试过。
为什么在 JavaScript 中同时执行Object instanceof Function和Function instanceof Object返回true?
我在 Safari WebInspector 中尝试过。
我花了一段时间才弄明白,但这真的值得花时间。首先,让我们看看它是如何instanceof工作的。
引自MDN,
所述
instanceof操作者测试对象是否在其原型链具有prototype一个构造的属性。
[instanceof]现在,让我们看看instanceofECMA 5.1 规范是如何定义的,
生产
RelationalExpression: RelationalExpression instanceof ShiftExpression评估如下:
- 让
lref成为评估的结果RelationalExpression。- 我们
lval是GetValue(lref)。- 让
rref成为评估的结果ShiftExpression。- 我们
rval是GetValue(rref)。- 如果
Type(rval)不是对象,则抛出TypeError异常。- 如果
rval没有[[HasInstance]]内部方法,则抛出TypeError异常。- 返回调用with 参数的
[[HasInstance]]内部方法的结果。rvallval
首先评估左侧和右侧的表达式 ( GetValue),然后右侧的结果应该是一个带有[[HasInstance]]内部方法的对象。不是所有的对象都有[[HasInstance]]内部方法,但有函数。例如,以下将失败
console.log(Object instanceof {});
# TypeError: Expecting a function in instanceof check, but got #<Object>
[[HasInstance]]现在,让我们看看[[HasInstance]]ECMA 5.1 规范中是如何定义的,
假设
F是一个函数对象。当使用 value 调用的
[[HasInstance]]内部方法时,执行以下步骤:FV
- 如果
V不是对象,则返回false。- 让
O是调用具有属性名称的[[Get]]内部方法的结果。F"prototype"- 如果
Type(O)不是对象,则抛出TypeError异常。- 重复
- 让
V成为 的[[Prototype]]内部属性的值V。- 如果
V是null,则返回false。- 如果
O和V引用同一个对象,则返回true。
就这么简单。取prototype的财产F,并将其与比较[[Prototype]]的内部属性O,直到它变成null或prototype的F是一样的O。
[[prototype]] 内部财产首先让我们看看什么是[[prototype]]内部属性,
所有对象都有一个名为 的内部属性
[[Prototype]]。该属性的值是或者是null一个对象,用于实现继承。本机对象是否可以拥有宿主对象[[Prototype]]取决于实现。每条[[Prototype]]链都必须有有限的长度(即从任何对象开始,递归访问[[Prototype]]内部属性最终必须得到一个null值)。
注意:我们可以通过Object.getPrototypeOf函数获取这个内部属性。
prototype 财产[[HasInstance]]还讨论了另一个称为 的属性prototype,它是特定于Function对象的。
在将 Function 对象作为新创建对象的构造函数调用之前,该
prototype属性的值用于初始化[[Prototype]]新创建对象的内部属性。
这意味着,当函数对象用作构造函数时,将创建一个新对象,并且新对象的内部[[Prototype]]将使用此prototype属性进行初始化。例如,
function Test() {}
Test.prototype.print = console.log;
console.log(Object.getPrototypeOf(new Test()) === Test.prototype);
# true
现在让我们回到实际的问题。让我们看第一种情况
console.log(Object instanceof Function);
# true
它将Function.prototype首先获取并尝试查找该对象是否在 的原型层次结构中Object。让我们看看结果如何
console.log(Function.prototype);
# [Function: Empty]
console.log(Object.getPrototypeOf(Object));
# [Function: Empty]
console.log(Object.getPrototypeOf(Object) === Function.prototype);
# true
由于Function.prototype匹配Object的内部属性[[Prototype]],它返回true。
现在让我们来看第二种情况
console.log(Function instanceof Object);
# true
console.log(Object.prototype);
# {}
console.log(Object.getPrototypeOf(Function));
# [Function: Empty]
console.log(Object.getPrototypeOf(Function) === Object.prototype);
# false
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function)));
# {}
Object.getPrototypeOf(Object.getPrototypeOf(Function)) === Object.prototype
# true
在这里,首先我们得到Object.prototype,即{}。现在它正在尝试查找的原型链中{}是否存在相同的对象Function。Functionis 和 Empty 函数的直接父级。
console.log(Object.getPrototypeOf(Function));
# [Function: Empty]
它与 Object.prototype
console.log(Object.getPrototypeOf(Function) === Object.prototype);
# false
但[[HasInstance]]算法并不止于此。它重复并上升一级
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function)));
# {}
这与Object.prototype. 这就是为什么返回true.
来自MDN:
instanceof 运算符测试对象的原型链中是否具有构造函数的原型属性。
本质上,它正在检查Object(不是 的实例Object,而是构造函数本身)是否具有作为Function.constructor其原型链上某处的实例。
而且,确实:
> Function.__proto__.__proto__ === Object.prototype
true
> Object.__proto__ === Function.prototype
true
这解释了为什么Object instanceof Function以及相反的原因。
所有对象都有一个称为 [[Prototype]] 的内部属性。该属性的值为空或对象,用于实现继承。如果您尝试查找对象上的键但未找到,JavaScript 将在原型链中查找它。
Function 构造函数创建新的 Function 对象(Function 构造函数的实例)。原型属性特定于 Function 对象。Function 构造函数本身就是一个 Function 对象(Function 构造函数的实例)。
当 Function 对象用作构造函数时,将创建一个新对象,并且新对象的 [[Prototype]] 将使用构造函数的prototype 属性进行初始化。
function Dog () {}
var myCrazyDog = new Dog();
myCrazyDog.__proto__ === Dog.prototype // true
语言规范是所有对象都是 Object 构造函数的实例,所有函数都是 Function 构造函数的实例。
Object instanceof Function 为真,因为 Object 是一个函数,因此是 Function 的一个实例(Object 是一个 Function 对象 - Function 构造函数的一个实例)。对象继承自 Function.prototype。
console.log(Object instanceof Function) // true
console.log(Object.__proto__ === Function.prototype) // true
Object instanceof Object 为真,因为 Object 继承自 Function.prototype。由于 Function.prototype 是一个对象,它继承自 Object.prototype。 Object 的函数实例为真,因为Function 继承自Function.prototype。由于 Function.prototype 是一个对象,它继承自 Object.prototype。原型链如下所示:
Object ---> Function.prototype ---> Object.prototype ---> null
Function ---> Function.prototype ---> Object.prototype ---> null
console.log(Object instanceof Object) // true
console.log(Object.__proto__ === Function.prototype) // true
console.log(Object.__proto__.__proto__ === Object.prototype) // true
console.log(Function instanceof Object) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.__proto__.__proto__ === Object.prototype) // true
函数 instanceof 函数为 true。Function 是它自身的一个实例(自然,因为它是一个函数,因此也是 Function 的一个实例)。原型链如下所示:
Function ---> Function.prototype ---> Object.prototype ---> null
console.log(Function instanceof Function) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.__proto__.__proto__ === Object.prototype) // true
因此请记住,Function() 和 Object() 构造函数是函数。由于它们是函数,因此它们是 Function() 构造函数的实例并继承自 Function.prototype。由于Function.prototype是一个对象,Function.prototype是Object的一个实例,因此继承自Object.prototype。
console.log(Object instance of Function) // true
console.log(Function instance of Function) // true
console.log(Function.prototype instanceof Object); // true
您问题中混淆的根源在于 JavaScript (ECMAScript) 中函数* 的固有双重性质。
js 中的函数既是正则函数又是对象。将它们视为算法博士 Jekyll 和 Mr. Hyde。它们在外面看起来像对象,但在内部,它们只是具有所有怪癖的旧 js 函数,或者可能正好相反!
JavaScript 真的是一件棘手的事情 :)
所以回到你的问题,借用出现在 MDN 上的语法:
object instanceof constructor
将其应用于代码中的第一条语句:
Object instanceof Function
这里有Object一个构造函数,它用作对象初始值设定项,但由于函数在 js 中具有双重生命,因此它附加了特定于对象的 props 和方法,从而也有效地将其渲染为对象。
因此,语句中的第一个条件已满足。我们将继续调查其他条件或操作数。
Function 正如您可能已经注意到的,也是函数构造函数,但在执行此特定语句期间,我们现在对它的另一个对象方不感兴趣。
因此,句法条件都满足,即“对象”和“构造函数”。我们现在可以继续调查他们的遗传关系,以及他们之间是否存在联系。
由于Object它本身是一个工作函数,因此假设它的内部原型 prop 指向Function.prototype对象引用是很有意义的,因为在 js 中,所有函数都通过同一位置继承它们的 props 和方法Function.prototype。
true绝对是操作员执行此比较的唯一预期结果instanceof。
对于另一种情况:
Function instanceof Object
因为我们已经确定 js 中的函数也有对象的一面。他们从 中获得他们奇特的特定于对象的玩具是有道理的Object.prototype,因此它们构成 了 Object 构造函数的实例。
希望我的解释和寓言不会增加混乱。:)
*:不仅仅是在js中导致双重生活的函数。js 中的几乎所有数据类型都有一个对象的黑暗面,它们有助于轻松完成操作和操作。
最糟糕的属性实际上是 Function 是它自己的一个实例。Function instanceof Function返回真。
在 Kannan 的The Surprisingly Elegant Javascript Type Model 中有很好的解释,在http://web.archive.org/web/20140205182624/http://vijayan.ca/blog/2012/02/21/javascript-type-model
在解释的末尾引用:
是的,这意味着 Function 是它自己的一个实例(自然,因为它是一个函数,因此是 Function 的一个实例)。这是我们一直在处理的事情,无论有意与否,很长一段时间以来 - 所有构造函数都是常规函数,因此是 Function 的实例,而 Function 本身只是用于构造其他函数的构造函数,所以它也是函数的一个实例。