javascript - 在JavaScript中同时使用"Object instanceof Function" 和"Function instanceof Object" return true,为什么?

  显示原文与译文双语对照的内容

为什么在JavaScript中同时使用 Object instanceof FunctionFunction instanceof Object 返回 true

我在 Safari WebInspector里试用过。

时间: 原作者:

我花了一段时间才算出来,但花费的时间真宝贵。 首先,让我们看看 instanceof 是如何工作的。

引用 MDN

instanceof 操作符测试一个对象是否在它的Prototype链中的prototype 属性的属性。

[instanceof]

现在,让我们看看 instanceof 是如何由 ECMA 5.1规范定义的,

生产 RelationalExpression: RelationalExpression instanceof ShiftExpression 计算如下:

  1. lref 是评估 RelationalExpression的结果。
  2. lval 成为 GetValue(lref)
  3. rref 是评估 ShiftExpression的结果。
  4. rval 成为 GetValue(rref)
  5. 如果 Type(rval) 不是对象,抛出 TypeError 异常。
  6. 如果 rval 没有 [[HasInstance]] 内部方法,抛出 TypeError 异常。
  7. 返回调用 [[HasInstance]] 内部方法的结果与论点 lvalrval

首先,左边和右边的表达式被求值为( GetValue ),然后右手的结果应该是一个具有 [[HasInstance]] 内部方法的对象。 不是所有对象都有 [[HasInstance]] 内部方法,但函数。 例如以下将失败


console.log(Object instanceof {});
# TypeError: Expecting a function in instanceof check, but got #<Object>

[[HasInstance]]

现在,让我们看看 [[HasInstance]] 如何在 ECMA 5.1规范中定义,

假定 F 是一个函数对象。

[[HasInstance]] 内部的F 方法使用值 V 调用时,执行以下步骤:

  1. 如果 V 不是对象,返回 false
  2. O 调用 [[Get]] 内部方法的结果与属性名 "prototype"F
  3. 如果 Type(O) 不是对象,抛出 TypeError 异常。
  4. 重复
    1. VV[[Prototype]] 内部属性的值。
    2. 如果 Vnull,返回 false
    3. 如果 OV 引用同一个对象,返回 true

它很简单。获取 Fprototype 属性并将它的与 O[[Prototype]] 内部属性进行比较,直到它成为 Fnull 或者 prototype

[[Prototype]] 内部属性

首先让我们看看什么是 [[Prototype]] 内部属性

所有对象都有一个名为 [[Prototype]]的内部属性。 这里属性的值是 null 或者一个对象,用于实现继承。 本机对象是否可以拥有宿主对象取决于实现。 每个 [[Prototype]] 链必须有有限长度( 也就是说,从任何对象开始递归访问 [[Prototype]] 内部属性最终必须导致 null 值) 。

注意:我们可以得到这个内部属性与 Object.getPrototypeOf 函数。

prototype 属性

[[HasInstance]] 还谈到了另一个名为 prototype的属性,它特定于 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的Prototype层次结构中。 让我们看看结果如何


console.log(Function.prototype);
# [Function: Empty]
console.log(Object.getPrototypeOf(Object));
# [Function: Empty]
console.log(Object.getPrototypeOf(Object) === Function.prototype);
# true

自从 Function.prototype[[Prototype]]Object 内部的属性匹配,它将返回 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 Prototype中。 Function的直接父级是空函数。


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操作符测试一个对象是否在它的Prototype链中的Prototype的Prototype属性。

基本上,它正在检查 Object ( 不是 Object的实例,而是构造函数本身) 是否在它的Prototype链的某个地方作为 Function.constructor的实例。

而且,实际上:


> Function.__proto__.__proto__ === Object.prototype
true
> Object.__proto__ === Function.prototype
true

这解释了为什么 Object instanceof Function 以及反过来。

原作者:

混乱的来源你的问题在于在javascript( ECMAScript ) functions*固有的双重性质。

js中的函数同时是常规函数和对象。 把它们当作算法,医生。 and和 。 它们看起来像对象在外面里面却只是你美好的js函数与他们所有的怪癖,或者是相反的!

JavaScript实际上是一个棘手的业务:

回到你的问题,借用出现在MDN上的语法:

object instanceof constructor

在代码中的第一个语句上应用它:

Object instanceof Function

Object 这里,作为一个对象的构造函数初始化器但js函数过着双重人格的生活以来,它已经object-specific道具和方法附加到它呈现有效的对象。

因此,语句中的第一个条件已经满足。 我们将继续研究其他条件或者操作数。

Function 因为你可能已经注意到了函数构造函数,但它的另一个对象端在执行这个语句时没有兴趣。

因此,句法条件都满足,即"对象"和"构造函数"。 我们现在可以研究他们的遗传关系,如果它们之间有一个连接。

自从 Object 也算一个函数本身,它的一种很合理的做法是js中的一个假设,即都有它的内部Prototype道具指向 Function.prototype的对象引用,因为所有 函数 各种道具和继承方法通过该同一地点 Function.prototype

true 绝对是只预期结果执行的这种比较 instanceof 算子。

对于另一种情况:

Function instanceof Object

既然我们已经在js中建立了函数,那么它们也有一个对象边。 是有道理的,他们喜欢object-specific玩具 Object.prototype, 因此他们构成实例对象的构造函数。

希望我没有在我的解释和allegories中添加混乱。 : )

*: 不仅仅是在js中导致双重生命的函数。 几乎所有js中的数据类型都有一个对象的暗边,便于完成操作和操作,而。

原作者:
...