为什么在 JavaScript 中“Object instanceof Function”和“Function instanceof Object”都返回true?

IT技术 javascript function object
2021-02-02 18:07:12

为什么在 JavaScript 中同时执行Object instanceof FunctionFunction instanceof Object返回true

我在 Safari WebInspector 中尝试过。

6个回答

我花了一段时间才弄明白,但这真的值得花时间。首先,让我们看看它是如何instanceof工作的。

引自MDN

所述instanceof操作者测试对象是否在其原型链具有prototype一个构造的属性。

[instanceof]

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

生产RelationalExpression: RelationalExpression instanceof ShiftExpression评估如下:

  1. lref成为评估的结果RelationalExpression
  2. 我们lvalGetValue(lref)
  3. rref成为评估的结果ShiftExpression
  4. 我们rvalGetValue(rref)
  5. 如果Type(rval)不是对象,则抛出TypeError异常。
  6. 如果rval没有[[HasInstance]]内部方法,则抛出TypeError异常。
  7. 返回调用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

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

就这么简单。prototype的财产F,并将其与比较[[Prototype]]的内部属性O,直到它变成nullprototypeF是一样的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,即{}现在它正在尝试查找的原型链中{}是否存在相同的对象FunctionFunctionis 和 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.

如果别人也被弄得Object.getPrototypeOf在这里,你还可以把它当做Reflect.getPrototypeOf已添加到ES6规范。
2021-04-12 18:07:12

来自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 函数为 trueFunction 是它自身的一个实例(自然,因为它是一个函数,因此也是 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 本身只是用于构造其他函数的构造函数,所以它也是函数的一个实例。

在此处输入图片说明