【问题标题】:Why in JavaScript both "Object instanceof Function" and "Function instanceof Object" return true?为什么在 JavaScript 中“Object instanceof Function”和“Function instanceof Object”都返回 true?
【发布时间】:2014-05-13 04:14:13
【问题描述】:

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

我在 Safari WebInspector 中尝试过。

【问题讨论】:

  • 函数是对象,并不是所有的对象都是函数。检查实例适用于实例 - 您正在检查本身就是函数的构造函数。
  • 我很想知道您的预期结果。
  • @cookiemonster - 从问题中,OP 期望进行 isPrototypeOf 检查。
  • @BenjaminGruenbaum:你在问题的什么地方看到了?
  • 在潜台词中,从事实来看,OP 对 Object instanceof Function 感到惊讶。

标签: javascript function object


【解决方案1】:

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

引用MDN

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

[instanceof]

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

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

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

首先计算左侧和右侧表达式 (GetValue),然后右侧结果应该是带有 [[HasInstance]] 内部方法的对象。并非所有对象都有[[HasInstance]] 内部方法,而是函数。比如下面的会失败

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

[[HasInstance]]

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

假设F 是一个函数对象。

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

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

就是这么简单。将Fprototype属性与O[[Prototype]]内部属性进行比较,直到变成FnullprototypeO相同。

[[prototype]]内部属性

首先让我们看看[[prototype]] internal property是什么,

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

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

prototype属性

[[HasInstance]] 还谈到了另一个名为 prototype 的属性,它特定于 Function 对象。

prototype 属性的值用于在 Function 对象作为新创建对象的构造函数调用之前初始化新创建对象的 [[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 的原型链中。 Function 的直接父级 is 和 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 的规范中。
【解决方案2】:

来自MDN

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

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

而且,确实:

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

这解释了为什么Object instanceof Function 以及相反的原因。

【讨论】:

    【解决方案3】:

    所有对象都有一个称为 [[Prototype]] 的内部属性。此属性的值为 null 或对象,用于实现继承。如果您尝试查找对象上的键但未找到,JavaScript 将在原型链中查找它。

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

    当 Function 对象用作构造函数时,将创建一个新对象,并使用构造函数的原型属性初始化新对象的 [[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 为 true,因为 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
    

    Function instanceof Function 为真。 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
    

    【讨论】:

      【解决方案4】:

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

      js中的函数既是正则函数又是对象。将它们视为算法博士。杰基尔和海德先生。它们在外部看起来像对象,但在内部它们只是您的旧 js 函数,具有所有的怪癖,或者可能是相反的方式!

      JavaScript 真的是一门棘手的工作 :)

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

      object instanceof constructor

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

      Object instanceof Function

      这里有Object,一个构造函数,用作对象初始值设定项,但由于函数在 js 中具有双重生命,因此它附加了特定于对象的道具和方法,从而有效地将其渲染为对象。

      所以,语句中的第一个条件已经满足。我们继续调查其他条件或操作数。

      Function 您可能已经注意到,它也是函数构造函数,但在执行此特定语句期间,我们现在对它的另一个对象方面不感兴趣。

      所以,句法条件都满足,即“对象”和“构造函数”。我们现在可以继续调查他们的遗传关系,以及他们之间是否存在联系。

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

      true 绝对是instanceof 运算符执行的此比较的ONLY预期结果。

      其他情况:

      Function instanceof Object

      既然我们已经确定 js 中的函数也有对象的一面。他们从Object.prototype 获得了他们花哨的特定于对象的toys 是有道理的,因此它们构成了 Object 构造函数的instances

      希望我的解释和寓言没有造成混乱。 :)

      *: 不仅仅是在 js 中过着双重生活的函数。 js 中几乎所有的数据类型都有一个对象的阴暗面,便于轻松完成操作和操作。

      【讨论】:

        【解决方案5】:

        最糟糕的属性实际上是 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 本身只是构造其他函数的构造函数,所以它也是Function 的一个实例。

        【讨论】:

          【解决方案6】:

          非常简单的解释,与所有答案不同

          Object 和 Function 都是构造函数(两者的类型都返回“Function objects”)并且都是从 Function Constructor 创建的。 这两个的 __proto__ 属性都指向 'Function.prototype' 对象。

          快速解释:对象的 __proto__ 属性(例如,P1 是 Person 类型)指向构造函数的原型(例如 Person.prototype) 原型链中的 __proto__ 再次指向对象“Object.prototype”。

          在阅读更多关于 CHROME CONSOLE 的打印详细信息之前,console.dir(Object)、console.dir(Function)

          请记住,函数构造函数和对象,因此您将看到 .prototype 和 __proto__ 属性。在所有实例对象(比如 p1)中,您只会找到 __proto__ 属性。 __proto__ 是隐藏属性 [[Prototye]] 的访问器,最好的获取方法是 Object.getPrototypeOf(p1),因为 __proto__ 已被贬低。

          (p1 instanceof Person) 这里操作符检查构造函数Person的原型是否在对象p1的原型链中。请注意,第一个值是实例对象(p1),第二个值是构造函数(Person)。

          让我们分析一下 => Function instanceof Object.

          函数对象的 __proto__.__proto__ 指向 Object.prototype ,所以是真的

          让我们分析一下 => Object instanceof Function.

          Object对象的__proto__指向Function.prototype,所以是真的

          希望这会有所帮助。

          【讨论】:

            【解决方案7】:

            Object 是对象的内置构造函数。因此它是一个函数(Function 的一个实例)。函数是 JavaScript 中的对象,因此它也是一个对象(Object 的一个实例)。

            【讨论】:

              猜你喜欢
              • 2015-02-22
              • 2016-02-09
              • 1970-01-01
              • 2014-12-01
              • 2015-01-13
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多