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

为什么在JavaScript中, Object instanceof FunctionFunction instanceof Object返回true

我在Safari WebInspector中试了一下。

我花了一段时间才弄清楚,但真正值得花费的时间。 首先,让我们看看instanceof是如何工作的。

从MDN引用,

instanceof运算符testing对象是否在其原型链中具有构造函数的prototype属性。

[instanceof]

现在,让我们看看ECMA 5.1规范定义了instanceof

生产RelationalExpression: RelationalExpression instanceof ShiftExpression的计算方法如下:

  1. lref是评估RelationalExpression的结果。
  2. lval成为GetValue(lref)
  3. rref是评估ShiftExpression的结果。
  4. rvalGetValue(rref)
  5. 如果Type(rval)不是Object,则抛出一个TypeErrorexception。
  6. 如果rval没有[[HasInstance]]内部方法,则抛出一个TypeErrorexception。
  7. 返回用参数lval调用rval[[HasInstance]]内部方法的lval

首先评估左侧和右侧expression式( GetValue ),然后右侧结果应该是带有[[HasInstance]]内部方法的对象。 不是所有的对象都有[[HasInstance]]内部方法,但有函数。 例如,以下将失败

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

[[HasInstance]]

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

假设F是一个函数对象。

F[[HasInstance]]内部方法被调用值V ,采取以下步骤:

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

这是如此简单。 取Fprototype属性,并将其与O[[Prototype]]内部属性进行比较,直到它变为null或者F prototypeO相同。

[[prototype]]内部属性

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

所有对象都有一个名为[[Prototype]]的内部属性。 该属性的值是null或一个对象,用于实现inheritance。 本地对象是否可以将主对象作为其[[Prototype]]取决于实现。 每个[[Prototype]]链的长度必须是有限的(也就是说,从任何对象开始,recursion访问[[Prototype]]内部属性最终必须导致null值)。

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

prototype属性

[[HasInstance]]也谈到另一个称为prototype属性,它是Function对象的特定属性。

在调用Function对象作为新创build对象的构造函数之前, prototype属性的值用于初始化新创build对象的[[Prototype]]内部属性。

这意味着,当一个函数对象被用作构造函数时,将会创build一个新的对象,并且这个新对象将使用这个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.prototypeObject的内部属性[[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原型链中是否存在相同的对象。 Function即时父母是和空function。

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

它与Object.prototype不一样

 console.log(Object.getPrototypeOf(Function) === Object.prototype); # false 

但是[[HasInstance]]algorithm并不止于此。 它重复和起来了一个更高的水平

 console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function))); # {} 

这和Object.prototype是一样的。 这就是为什么这返回true

来自MDN :

instanceof运算符testing对象是否在其原型链中具有构造函数的原型属性。

本质上,它检查Object (不是Object一个实例,而是构造函数本身)是否作为Function.constructor一个实例在原型链的某处。

而且的确如此:

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

这就解释了为什么Object instanceof Function也是相反的。

在你的问题混乱的根源在于JavaScript(ECMAScript)中函数的固有的双重性质。

js中的函数同时是常规函数和对象。 把他们看作算法博士杰基尔和海德先生 。 他们看起来像外面的东西,但在里面,他们只是你的老jsfunction与所有的怪癖,或者也许是相反的方式!

JavaScript真的是一个棘手的业务:)

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

object instanceof constructor

将其应用于代码中的第一条语句上:

Object instanceof Function

这里有Object ,一个用作对象初始值设定项的构造函数,但是由于函数在js中引入了双重生命,所以它具有特定于对象的道具和方法,使得它有效地成为一个对象。

因此,声明中的第一个条件已经得到满足。 我们仍然在调查其他条件或操作数。

你可能已经注意到的函数也是函数的构造函数,但是在执行这个特定的语句的时候,它的另一个对象是我们现在不感兴趣的。

因此,句法条件既是“对象”又是“构造者”。 我们现在可以继续调查它们的世袭关系,以及它们之间是否有联系。

因为Object本身就是一个工作函数,所以假设它具有指向Function.prototype对象引用的内部原型道具是很有意义的,因为在js中, 所有函数通过同一个位置Function.prototype inheritance它们的道具和方法。

true是绝对algorithm运算符执行此比较的唯一预期结果。

对于其他情况:

Function instanceof Object

既然我们已经确定js中的函数对他们也有一个客观的一面。 从Object.prototype得到他们喜欢的对象特定的玩具Object.prototype ,因此它们构成了Object构造函数的实例

希望我没有添加与我的解释和寓言混淆。 🙂

*: 不仅是导致js中双重生活的function。 js中的几乎所有的数据types都有一个对象的黑暗面,以方便完成操作和操作而没有任何麻烦。

最坏的财产实际上是function本身的一个实例。 Function instanceof Function返回true。

在Kannan的“令人惊讶的优雅的Javascripttypes模型”中可以很好地解释, http: //web.archive.org/web/20140205182624/http://vijayan.ca/blog/2012/02/21/javascript-type-model

在解释结尾引用:

是的,这意味着Function是其自身的一个实例(自然,因为它是一个函数,因此也是函数的一个实例)。 这是我们已经有意或无意地处理了很长一段时间 – 所有的构造函数都是规则的函数,因此函数的实例,而函数本身只是构造其他函数的构造函数,所以它也是函数的一个实例。

在这里输入图像描述

所有对象都有一个名为[[Prototype]]的内部属性。 该属性的值是null或一个对象,用于实现inheritance。 如果您尝试查找对象上的某个键,但未find该键,则JavaScript将在原型链中查找该键。

Function构造函数创build新的Function对象(Function构造函数的实例)。 原型属性是特定于Function对象的。 Function构造函数本身就是一个Function对象(Function构造函数的实例)。

当一个Function对象被用作构造函数时,将会创build一个新的对象,并且新的对象将使用构造函数的prototype属性初始化其[[Prototype]]。

 function Dog () {} var myCrazyDog = new Dog(); myCrazyDog.__proto__ === Dog.prototype // true 

语言规范是所有对象都是Object构造函数的实例,所有函数都是Function构造函数的实例。

Object instanceof Function是true,因为Object是一个函数,因此是Function(Object是Function对象 – Function构造函数的一个实例)的一个实例。 对象从Function.prototypeinheritance。

 console.log(Object instanceof Function) // true console.log(Object.__proto__ === Function.prototype) // true 

Object instanceof Object是true,因为Objectinheritance自Function.prototype。 由于Function.prototype是一个对象,它inheritance自Object.prototype。 Object的函数实例是真的,因为Function从Function.prototypeinheritance。 由于Function.prototype是一个对象,它inheritance自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 Function是true 。 函数本身就是一个实例(自然,因为它是一个函数,因此也是函数的一个实例)。 原型链看起来像这样:

 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.prototypeinheritance。 由于Function.prototype是一个对象,因此Function.prototype是Object的一个实例,因此inheritance自Object.prototype。

 console.log(Object instance of Function) // true console.log(Function instance of Function) // true console.log(Function.prototype instanceof Object); // true