【问题标题】:What is the instanceof operator in JavaScript?JavaScript 中的 instanceof 运算符是什么?
【发布时间】:2011-01-27 19:13:23
【问题描述】:

JavaScript 中的 instanceof 关键字在第一次遇到时可能会让人很困惑,因为人们倾向于认为 JavaScript 不是一种面向对象的编程语言。

  • 这是什么?
  • 它解决了什么问题?
  • 什么时候合适,什么时候不合适?

【问题讨论】:

  • 虽然下面的答案非常有用,但在实际应用程序中您并没有使用太多(至少我没有)。可以创建一个 object.type 属性,该属性包含一个字符串并对其进行检查。
  • JS 毫无意义:"foo" instanceof String => false,1 instanceof Number => false,{} instanceof Object => false。说什么?!
  • @morbusg 您的评论具有误导性。首先"foo" instanceof String => false 是正确的,因为typeof "foo" == 'string'new String("foo") instanceof String => true,因为typeof String == 'function' - 你应该像对待类一样对待函数(类的定义)。当您将变量分配为var v = new AnythingWhatTypeofEqualsFunction() 时,变量变为instanceof 一些function(类)。这同样适用于1typeof 1 == 'number' - 'number' 不是 'function' :) 下一个 - {} instanceof Object 在 node 和现代浏览器中是 TRUE
  • @fider:那是对语言规范的评论,来自 ruby​​ist。
  • @morbusg - ({}) instanceof Object 将返回 true。事实上你写的代码会给你一个错误。

标签: javascript operators instanceof


【解决方案1】:

实例

左侧 (LHS) 操作数是被测试到右侧 (RHS) 操作数的实际对象,右侧 (RHS) 操作数是类的实际构造函数。基本定义是:

Checks the current object and returns true if the object
is of the specified object type.

这里有一些good examples,这是一个直接取自Mozilla's developer site的例子:

var color1 = new String("green");
color1 instanceof String; // returns true
var color2 = "coral"; //no type specified
color2 instanceof String; // returns false (color2 is not a String object)

值得一提的是,如果对象继承自类的原型,instanceof 的计算结果为 true:

var p = new Person("Jon");
p instanceof Person

p instanceof Person 为真,因为p 继承自Person.prototype

根据 OP 的要求

我添加了一个带有一些示例代码和解释的小示例。

当你声明一个变量时,你给它一个特定的类型。

例如:

int i;
float f;
Customer c;

上面显示了一些变量,即ifc。类型为integerfloat 和用户定义的Customer 数据类型。上述类型可以用于任何语言,而不仅仅是 JavaScript。但是,在 JavaScript 中,当您声明一个变量时,您不会显式定义类型,var x,x 可以是数字/字符串/用户定义的数据类型。那么instanceof 所做的是检查对象以查看它是否属于指定的类型,因此从上面获取我们可以做的Customer 对象:

var c = new Customer();
c instanceof Customer; //Returns true as c is just a customer
c instanceof String; //Returns false as c is not a string, it's a customer silly!

在上面我们已经看到c 被声明为Customer 类型。我们已经新建了它并检查了它是否属于Customer 类型。当然,它返回 true。然后仍然使用Customer 对象,我们检查它是否是String。不,绝对不是String,我们新建了Customer 对象而不是String 对象。在这种情况下,它返回 false。

真的就是这么简单!

【讨论】:

  • @Alon - 我为你添加了一个例子。见上文,color1 是字符串类型,所以当你说color1 instanceof String; 时,这将返回 true,因为 color1 是一个字符串。
  • @Alon - 它检查一个对象以查看它是什么类型的对象。考虑一个人/客户对象。所以person p = new person() p 现在是人类型而不是字符串类型。
  • 在 JavaScript 中,理解变量在其整个生命周期内具有不同类型的灵活性可能会很棘手。代码示例:jsfiddle.net/sikusikucom/znSPv
  • @Siku-Siku.Com - 我看不出有什么技巧var zero = 0; alert(zero); zero = "0"; alert(zero) 我们从原始int 到原始string 没有任何问题。
  • 哦,我的意思是 理解(不是)JavaScript 中的“灵活性”是棘手的/新的,尤其是。对于一些习惯于需要例如语言的人来说显式转换以更改变量类型。正如您所指出的,从例如更改类型非常容易。原始int 到原始string
【解决方案2】:
//Vehicle is a function. But by naming conventions
//(first letter is uppercase), it is also an object
//constructor function ("class").
function Vehicle(numWheels) {
    this.numWheels = numWheels;
}

//We can create new instances and check their types.
myRoadster = new Vehicle(4);
alert(myRoadster instanceof Vehicle);

【讨论】:

    【解决方案3】:

    我认为值得注意的是,instanceof 是通过在声明对象时使用“new”关键字定义的。在 JonH 的例子中;

    var color1 = new String("green");
    color1 instanceof String; // returns true
    var color2 = "coral";
    color2 instanceof String; // returns false (color2 is not a String object)
    

    他没有提到的是这个;

    var color1 = String("green");
    color1 instanceof String; // returns false
    

    指定“new”实际上是将 String 构造函数的结束状态复制到 color1 var 中,而不是仅仅将其设置为返回值。我认为这更好地显示了新关键字的作用;

    function Test(name){
        this.test = function(){
            return 'This will only work through the "new" keyword.';
        }
        return name;
    }
    
    var test = new Test('test');
    test.test(); // returns 'This will only work through the "new" keyword.'
    test // returns the instance object of the Test() function.
    
    var test = Test('test');
    test.test(); // throws TypeError: Object #<Test> has no method 'test'
    test // returns 'test'
    

    使用“new”将函数内“this”的值分配给声明的var,而不使用它则分配返回值。

    【讨论】:

    • new 与任何 JavaScript 类型一起使用是没有意义的,这会使接受的答案对初学者更加困惑。 text = String('test')options = {} 不会由 instanceof 测试,而是由 typeof 代替。
    • Date.getTime() // 失败。是的,新的很重要。
    • 尝试在控制台中运行它。你会得到一个错误,因为 getTime() 不存在。你确实需要使用新的。
    【解决方案4】:

    你可以用它来进行错误处理和调试,像这样:

    try{
        somefunction();
    } 
    catch(error){
        if (error instanceof TypeError) {
            // Handle type Error
        } else if (error instanceof ReferenceError) {
            // Handle ReferenceError
        } else {
            // Handle all other error types
        }
    }
    

    【讨论】:

      【解决方案5】:

      instanceof 的一个重要方面目前似乎没有在任何 cmets 中涵盖:继承。由于原型继承,使用 instanceof 评估的变量可能会为多个“类型”返回 true。

      例如,让我们定义一个类型和一个子类型:

      function Foo(){ //a Foo constructor
          //assign some props
          return this;
      }
      
      function SubFoo(){ //a SubFoo constructor
          Foo.call( this ); //inherit static props
          //assign some new props
          return this;
      }
      
      SubFoo.prototype = Object.create(Foo.prototype); // Inherit prototype
      SubFoo.prototype.constructor = SubFoo;
      

      现在我们有了几个“类”,让我们创建一些实例,并找出它们的实例:

      var 
          foo = new Foo()
      ,   subfoo = new SubFoo()
      ;
      
      alert( 
          "Q: Is foo an instance of Foo? "
      +   "A: " + ( foo instanceof Foo ) 
      ); // -> true
      
      alert( 
          "Q: Is foo an instance of SubFoo? " 
      +   "A: " + ( foo instanceof SubFoo ) 
      ); // -> false
      
      alert( 
          "Q: Is subfoo an instance of Foo? "
      +   "A: " + ( subfoo instanceof Foo ) 
      ); // -> true
      
      alert( 
          "Q: Is subfoo an instance of SubFoo? "
      +   "A: " + ( subfoo instanceof SubFoo ) 
      ); // -> true
      
      alert( 
          "Q: Is subfoo an instance of Object? "
      +   "A: " + ( subfoo instanceof Object ) 
      ); // -> true
      

      看到最后一行了吗?对函数的所有“新”调用都返回一个继承自 Object 的对象。即使使用对象创建简写也是如此:

      alert( 
          "Q: Is {} an instance of Object? "
      +   "A: " + ( {} instanceof Object ) 
      ); // -> true
      

      那么“类”定义本身呢?它们是什么实例?

      alert( 
          "Q: Is Foo an instance of Object? "
      +   "A:" + ( Foo instanceof Object) 
      ); // -> true
      
      alert( 
          "Q: Is Foo an instance of Function? "
      +   "A:" + ( Foo instanceof Function) 
      ); // -> true
      

      我觉得理解任何对象都可以是 MULTIPLE 类型的实例很重要,因为您(错误地)假设您可以通过使用 instanceof 来区分对象和函数。正如最后一个示例清楚地显示了一个函数一个对象。

      如果您使用任何继承模式并希望通过鸭子类型以外的方法确认对象的后代,这也很重要。

      希望对探索instanceof的人有所帮助。

      【讨论】:

      • 更棒的是,在你用SubFoo.prototype = new Foo();从原型继承之后,你可以向它添加更多的方法,subfoo instanceof Foo的检查仍然会和subfoo instanceof SubFoo一样通过
      【解决方案6】:

      这里的其他答案是正确的,但他们没有深入了解instanceof 的实际工作原理,这可能会引起一些语言律师的兴趣。

      JavaScript 中的每个对象都有一个原型,可通过__proto__ 属性访问。函数还有一个prototype 属性,它是它们创建的任何对象的初始__proto__。当一个函数被创建时,它会被赋予一个prototype 的唯一对象。 instanceof 运算符使用这种唯一性给你一个答案。如果您将 instanceof 编写为函数,这就是它的样子。

      function instance_of(V, F) {
        var O = F.prototype;
        V = V.__proto__;
        while (true) {
          if (V === null)
            return false;
          if (O === V)
            return true;
          V = V.__proto__;
        }
      }
      

      这基本上是对 ECMA-262 5.1 版(也称为 ES5)第 15.3.5.3 节的解释。

      请注意,您可以将任何对象重新分配给函数的prototype 属性,并且可以在构造对象后重新分配对象的__proto__ 属性。这会给你一些有趣的结果:

      function F() { }
      function G() { }
      var p = {};
      F.prototype = p;
      G.prototype = p;
      var f = new F();
      var g = new G();
      
      f instanceof F;   // returns true
      f instanceof G;   // returns true
      g instanceof F;   // returns true
      g instanceof G;   // returns true
      
      F.prototype = {};
      f instanceof F;   // returns false
      g.__proto__ = {};
      g instanceof G;   // returns false
      

      【讨论】:

      • 值得注意的是,IE中不允许直接操作“__proto__”属性。如果我没记错的话,ECMA 规范中也不包含对属性的直接操作,因此将其用于学术活动以外的作业可能是个坏主意。
      • @webnesto,没错,proto 不在规范中。我不知道 IE 不支持它。当您说直接操作时,您的意思是它根本不暴露给 JS 代码,或者它只是不可写?
      • 在旧版本上不能 100% 确定。从这里 (stackoverflow.com/questions/8413505/proto-for-ie9-or-ie10) 听起来,在 IE9 中它至少是可读的(尽管不是可变的)。同样值得注意的是,听起来浏览器可能会完全放弃它。
      • 理解 proto 属性的隐含存在对于用户代码是否可以访问都很重要。 +10 如果我可以引用规范,这正是我来这里寻找的。​​span>
      • 为了得到原型链接,请使用Object.getPrototypeOf(o),这将与您描述的__proto__相同,但符合ECMAScript。
      【解决方案7】:

      关于“什么时候合适,什么时候不合适?”这个问题,我的 2 美分:

      instanceof 在生产代码中很少有用,但在您想要断言代码返回/创建正确类型的对象的测试中很有用。通过明确您的代码返回/创建的对象类型,您的测试作为理解和记录代码的工具变得更加强大。

      【讨论】:

        【解决方案8】:

        我刚刚找到了一个真实的应用程序,我想现在会更频繁地使用它。

        如果您使用 jQuery 事件,有时您想编写一个更通用的函数,该函数也可以直接调用(不带事件)。您可以使用instanceof 来检查您的函数的第一个参数是否是jQuery.Event 的实例并做出适当的反应。

        var myFunction = function (el) {                
            if (el instanceof $.Event) 
                // event specific code
            else
                // generic code
        };
        
        $('button').click(recalc);    // Will execute event specific code
        recalc('myParameter');  // Will execute generic code
        

        在我的例子中,该函数需要为所有(通过按钮上的点击事件)或只计算一个特定元素。我使用的代码:

        var recalc = function (el) { 
            el = (el == undefined || el instanceof $.Event) ? $('span.allItems') : $(el);
            // calculate...
        };
        

        【讨论】:

          【解决方案9】:

          instanceof 只是isPrototypeOf 的语法糖:

          function Ctor() {}
          var o = new Ctor();
          
          o instanceof Ctor; // true
          Ctor.prototype.isPrototypeOf(o); // true
          
          o instanceof Ctor === Ctor.prototype.isPrototypeOf(o); // equivalent
          

          instanceof 只依赖于对象的构造函数原型。

          构造函数只是一个普通的函数。严格来说它是一个函数对象,因为在 Javascript 中一切都是对象。而这个函数对象有一个原型,因为每个函数都有一个原型。

          原型只是一个普通对象,它位于另一个对象的原型链中。这意味着在另一个对象的原型链中使一个对象成为原型:

          function f() {} //  ordinary function
          var o = {}, // ordinary object
           p;
          
          f.prototype = o; // oops, o is a prototype now
          p = new f(); // oops, f is a constructor now
          
          o.isPrototypeOf(p); // true
          p instanceof f; // true
          

          应该避免使用instanceof 运算符,因为它会伪造 Javascript 中不存在的类。尽管 class 关键字在 ES2015 中也没有,因为 class 再次只是语法糖......但这是另一个故事。

          【讨论】:

          • 第一行不正确!有关更多信息,请参阅here:“isPrototypeOf() 与 instanceof 运算符不同。在表达式“object instanceof AFunction”中,对象原型链是针对 AFunction.prototype 而不是针对 AFunction 本身进行检查的。”
          • @Yan 而instanceof 仍然源自isPrototypeOf。我称之为语法糖。而且你的 MDN 来源是个笑话,不是吗?
          • 没有。这不是也不应该是一个玩笑,仍然是错误的链接。我本来打算发布this one。我不想涉及技术细节,但instanceof 做与isPrototypeOf 相同的事情。就是这样。
          【解决方案10】:

          这是什么?

          Javascript 是一种原型语言,这意味着它使用原型进行“继承”。 instanceof 运算符测试构造函数的 prototype propertype 是否存在于对象的 __proto__ 链中。这意味着它将执行以下操作(假设 testObj 是一个函数对象):

          obj instanceof testObj;
          
          1. 检查对象的原型是否等于构造函数的原型:obj.__proto__ === testObj.prototype >> 如果这是true instanceof 将返回true
          2. 将爬上原型链。例如:obj.__proto__.__proto__ === testObj.prototype >> 如果这是true instanceof 将返回true
          3. 将重复步骤 2,直到检查完对象的完整原型。如果对象的原型链上没有任何地方与testObj.prototype 匹配,则instanceof 运算符将返回false

          示例:

          function Person(name) {
            this.name = name;
          }
          var me = new Person('Willem');
          
          console.log(me instanceof Person); // true
          // because:  me.__proto__ === Person.prototype  // evaluates true
          
          console.log(me instanceof Object); // true
          // because:  me.__proto__.__proto__ === Object.prototype  // evaluates true
          
          console.log(me instanceof Array);  // false
          // because: Array is nowhere on the prototype chain

          它解决了什么问题?

          它解决了方便检查对象是否源自某个原型的问题。例如,当一个函数接收到一个可以有各种原型的对象时。然后,在使用原型链中的方法之前,我们可以使用instanceof 运算符来检查这些方法是否在对象上。

          示例:

          function Person1 (name) {
            this.name = name;
          }
          
          function Person2 (name) {
            this.name = name;
          }
          
          Person1.prototype.talkP1 = function () {
            console.log('Person 1 talking');
          }
          
          Person2.prototype.talkP2 = function () {
            console.log('Person 2 talking');
          }
          
          
          function talk (person) {
            if (person instanceof Person1) {
              person.talkP1();
            }
            
            if (person instanceof Person2) {
              person.talkP2();
            }
            
            
          }
          
          const pers1 = new Person1 ('p1');
          const pers2 = new Person2 ('p2');
          
          talk(pers1);
          talk(pers2);

          这里在talk() 函数中首先检查原型是否位于对象上。在此之后,选择适当的方法来执行。不执行此检查可能会导致执行不存在的方法,从而导致引用错误。

          什么时候合适,什么时候不合适?

          我们已经讨论过了。当您需要在使用对象之前检查对象的原型时使用它。

          【讨论】:

          • 我想你想写的不是“构造函数的原型出现在构造函数的原型属性中的某处
          • 您可能想提一下,让人们共享一个接口并将这两个方法命名为PersonX.prototype.talk 会更合适,这样talk 函数就可以简单地执行person.talk()
          • 你完全正确地更新了它,所以定义更好。谢谢指出!
          • 另外,不要在文档中使用 __proto__,它已被弃用 - 请改写 Object.getPrototype()
          【解决方案11】:

          @SebastianSimon 我的最后一个答案是 8 岁(当我跛脚时),我可能写了一些公牛*命中 :)

          简而言之 - 目前我使用 instanceof 的唯一情况是我使用 class 实例并且行为取决于我将收到的类,例如。我想区分 404 是 ErrorA(resource not exist) 还是 ErrorB(service not found) - 库响应代码令人困惑,但幸运的是它使用不同的错误类抛出。

          绝对(目前)我不会使用它来检查反映原语的类型 - 您无法确定库是返回 'msg' 还是 new String('msg')

          它们都有属于String 类的方法,因为'msg' 原语在内部被包装到字符串对象中。内部意味着解释器。它们都是Strings,但instanceof 运算符在这里似乎不够——检查是否。是原始的或类我会使用 typeof && instanceof 的组合 - 但仅适用于从外部 JS 库返回的东西。

          当前的 TypeScript 正在解决这个问题,您不再需要对 typeofinstanceof 进行如此繁琐的检查。

          【讨论】:

            猜你喜欢
            • 2011-11-10
            • 1970-01-01
            • 2016-08-28
            • 2011-05-11
            • 2012-12-29
            • 2019-07-01
            • 2011-05-30
            • 2014-03-24
            • 1970-01-01
            相关资源
            最近更新 更多