【问题标题】:What's the difference between isPrototypeOf and instanceof in Javascript?Javascript中的isPrototypeOf和instanceof有什么区别?
【发布时间】:2011-01-28 16:43:00
【问题描述】:

在我自己的一些旧代码中,我使用以下代码:

Object.prototype.instanceOf = function( iface )
{
 return iface.prototype.isPrototypeOf( this );
};

然后我做(例如)

[].instanceOf( Array )

这可行,但似乎以下内容也可以:

[] instanceof Array

现在,这当然只是一个非常简单的例子。因此,我的问题是:

a instanceof b总是b.prototype.isPrototypeOf(a)一样吗?

【问题讨论】:

  • 虽然您始终可以使用 instanceof(在 RHS 上使用构造函数),但并非所有对象都可以从 Object.prototype 继承。 Object.create(null) instanceof Something({}).instanceOf({prototype:Something.prototype}) 将在反向失败的情况下工作(并产生 false)。

标签: javascript inheritance prototype instanceof


【解决方案1】:

a instanceof b 总是与b.prototype.isPrototypeOf(a) 相同吗?

不,a instanceof b 的行为并不总是与 b.prototype.isPrototypeOf(a) 相同。

CMS' answer 指出它们的区别在于它们是什么(一个是运算符,另一个是Object.prototype 对象上可用的内置方法)。这是正确的,但也有一些特殊情况,a instanceof b 将导致 TypeErrorb.prototype.isPrototypeOf(a) 将正常工作,反之亦然。

差异 #1

instanceof 的右侧应该是构造函数。

如果b 不是函数:

  • a instanceof b 将产生 TypeError

  • b.prototype.isPrototypeOf(a) 可以正常工作。

const b = {
    prototype: {}
};
const a = Object.create( b.prototype );

console.log( b.prototype.isPrototypeOf(a) );    // true
console.log( a instanceof b );                  // TypeError: Right-hand side of 'instanceof' is not callable

区别 #2

使用b.prototype.isPrototypeOf(a)时,b.prototype应该继承自Object.prototype

如果b.prototype 无法访问Object.prototype.isPrototypeOf() 方法:

  • b.prototype.isPrototypeOf(a) 将产生 TypeError
  • a instanceof b 可以正常工作。

function B() {};
B.prototype = Object.create( null );

const a = new B();

console.log( a instanceof B );              // true
console.log( B.prototype.isPrototypeOf(a) ) // TypeError: B.prototype.isPrototypeOf is not a function

区别 #3

如果instanceof 的右侧是绑定函数,则将其等同于其目标函数。

如果 b 是绑定函数:

  • a instanceof b 可以正常工作。
  • b.prototype.isPrototypeOf(a) 将产生 TypeError(绑定函数没有 prototype 属性)。

function B() {};
const BoundB = B.bind( null );
const a = new B();

console.log( a instanceof BoundB );              // true
console.log( BoundB.prototype.isPrototypeOf(a) ) // TypeError: Cannot read property 'isPrototypeOf' of undefined

结论

  • 如果您正在处理通过Object.create() 建立的原型继承,而不使用构造函数,您可能应该使用Object.prototype.isPrototypeOf() 方法(实际上instanceof 的用例受到更多限制,因为instanceof 期望其右侧参数为构造函数)。
  • 如果您正在处理构造函数,使用instanceof 运算符会稍微安全一些(您将能够涵盖绑定函数以及Object.prototype 不在Constructor.prototype 的原型链中的情况)。

【讨论】:

  • 请注意,Difference #2 中遇到的问题也可以通过使用Object.prototype.isPrototypeOf.call(B.prototype, a) 来避免
【解决方案2】:

运算符优先级和真实性不同,因为一个是表达式,另一个是方法调用。需要强调的一点是,两者都遍历原型链,因此您不能假设匹配的原型和相关对象之间存在一对一的映射:

var i = 0;

function foo()
{
console.log("foo");
console.log(i++ + ": " + Object.prototype.isPrototypeOf(Object) ) //true
console.log(i++ + ": " + Function.prototype.isPrototypeOf(Function) ) //true

console.log(i++ + ": " + Function.prototype.isPrototypeOf(Function) ) //true
console.log(i++ + ": " + Function.prototype.isPrototypeOf(Object) ) //true

console.log(i++ + ": " + RegExp.prototype.isPrototypeOf( RegExp(/foo/) ) ) //true
console.log(i++ + ": " + Object.prototype.isPrototypeOf( RegExp(/foo/) ) ) //true
console.log(i++ + ": " + Function.prototype.isPrototypeOf( RegExp(/foo/) ) ) //false
console.log(i++ + ": " + Object.prototype.isPrototypeOf(Math) ) //true
console.log(i++ + ": " + Math.isPrototypeOf(Math) ) //false
}

function bar()
{
console.log("bar");
console.log(i++ + ": " + (Object instanceof Object) ) //true

console.log(i++ + ": " + (Function instanceof Function) ) //true
console.log(i++ + ": " + (Function instanceof Object) ) //true

console.log(i++ + ": " + (RegExp(/foo/) instanceof RegExp) ) //true
console.log(i++ + ": " + (RegExp(/foo/) instanceof Object)  ) //true
console.log(i++ + ": " + (RegExp(/foo/) instanceof Function) ) //false
console.log(i++ + ": " + (Math instanceof Object) ) //true
console.log(i++ + ": " + (Math instanceof Math) ) //error
}
try
  {
  foo()
  }
catch(e)
  {
  console.log(JSON.stringify(e));
  }
finally
  {
  try
    {
    bar();
    }
  catch(e)
    {
    console.log(JSON.stringify(e));
    }
  }

参考文献

【讨论】:

    【解决方案3】:

    是的,它们做同样的事情,都遍历原型链,寻找其中的特定对象。

    两者之间的区别在于它们是什么,以及您如何使用它们,例如isPrototypeOfObject.prototype 对象上可用的一个函数,它可以让您测试一个特定对象 是否在另一个的原型链中,因为此方法已定义在Object.prototype 上,它可用于所有对象。

    instanceof一个运算符,它需要两个操作数,一个对象和一个Constructor function,它将测试传递的函数prototype 属性是否存在于对象的链上(通过[[HasInstance]](V) 内部操作,仅在 Function 对象中可用)。

    例如:

    function A () {
      this.a = 1;
    }
    function B () {
      this.b = 2;
    }
    B.prototype = new A();
    B.prototype.constructor = B;
    
    function C () {
      this.c = 3;
    }
    C.prototype = new B();
    C.prototype.constructor = C;
    
    var c = new C();
    
    // instanceof expects a constructor function
    
    c instanceof A; // true
    c instanceof B; // true
    c instanceof C; // true
    
    // isPrototypeOf, can be used on any object
    A.prototype.isPrototypeOf(c); // true
    B.prototype.isPrototypeOf(c); // true
    C.prototype.isPrototypeOf(c); // true
    

    【讨论】:

    • 所以,唯一的区别是,如果我只有原型,我可以使用 isPrototypeOf,而我需要 instanceof 的构造函数? (让我的函数真的和 instanceof 一样吗?)
    • 您的文字答案很有用,您的代码示例较少。 (它只显示相同的方面,但没有差异。)但是它向我提出了另一个问题:那是“.constructor”吗?我在某些地方看到过这样的代码,但我自己从来没有写过,而且我似乎不需要它。 (我通常有类似C.prototype.clazz = C; 的东西。)人们为什么要设置构造函数?
    • @Steffen Heil:我认为CMS编写的代码已经足够清晰了,它使用了最简单和已知(虽然不是最有效)的方式在构造函数之间继承JavaScript。尝试删除行 B.prototype.constructor = B 并检查 B 实例的构造函数:alert((new B).constructor),您将看到 A 函数构造函数。通过这样做,他保证自己找到 B 的所有实例的构造函数,只是 B。
    • @CMS 你也可以说,instanceof主要是为了伪经典继承,因为它依赖于B.prototypeB,这是一个“构造函数”。那么如果不使用B.prototypeB,那么就没有构造函数了,当JavaScript程序严格按照原型继承的方式编写的时候呢?在这种情况下,则可以使用animal.isPrototypeOf(woofie)
    • AFAIK instanceof 在不使用构造函数时不起作用,例如Object.create。似乎还有其他各种问题:tobyho.com/2011/01/28/checking-types-in-javascript
    猜你喜欢
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    • 1970-01-01
    • 2019-04-05
    • 2011-11-10
    • 1970-01-01
    • 2011-06-28
    • 2010-09-19
    相关资源
    最近更新 更多