JavaScript中的instanceof运算符是什么?

What is the instanceof operator in JavaScript?

javascript中的instanceof关键字在第一次遇到时会非常混乱,因为人们往往认为javascript不是面向对象的编程语言。

  • 这是怎么一回事?
  • 它能解决什么问题?
  • 什么时候合适,什么时候不合适?


运算符

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

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

下面是一些很好的例子,下面是直接从Mozilla的开发人员网站上获取的一个例子:

1
2
3
4
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的计算结果为真:

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

这是因为p继承了Person.prototype,所以p instanceof Person是正确的。

根据运营商的要求

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

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

例如:

1
2
3
int i;
float f;
Customer c;

上面显示了一些变量,即ifc。类型为integerfloat和用户定义的Customer数据类型。像上面这样的类型可以用于任何语言,而不仅仅是JavaScript。但是,使用javascript声明变量时,您没有显式定义类型,var x,x可以是数字/字符串/a用户定义的数据类型。因此,instanceof所做的是检查对象是否属于上面指定的类型,我们可以从上面获取Customer对象:

4

上面我们已经看到,c是用Customer类型声明的。我们做了新的,检查它是否是Customer型。当然,它返回真值。然后仍然使用Customer对象,我们检查它是否是String对象。不,绝对不是String我们换了一个Customer物体,不是String物体。在这种情况下,它返回false。

真的很简单!


到目前为止,这一例子有一个重要的方面似乎还没有被任何评论所涵盖:继承。由于原型继承,使用instanceof评估的变量可能会为多个"类型"返回true。

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

1
2
3
4
5
6
7
8
9
10
11
12
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 = new Foo(); // Inherit prototype

现在我们有了几个"类",让我们创建一些实例,并找出它们是什么实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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

看到最后一行了吗?对函数的所有"new"调用都返回从对象继承的对象。即使使用对象创建速记法,这也是正确的:

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

"类"定义本身又如何呢?它们是什么实例?

1
2
3
4
5
6
7
8
9
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

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

如果您使用的是任何继承模式,并且希望通过duck类型以外的方法来确认对象的子代,那么这一点也很重要。

希望能帮助任何人探索instanceof


这里的其他答案是正确的,但他们不了解instanceof的实际工作方式,这可能对外面的一些语言律师有意义。

javascript中的每个对象都有一个原型,可以通过__proto__属性访问。函数还有一个prototype属性,它是由它们创建的任何对象的初始__proto__属性。创建函数时,会为prototype提供一个唯一的对象。instanceof操作符使用这个唯一性来给您一个答案。这就是instanceof在编写函数时的样子。

1
2
3
4
5
6
7
8
9
10
11
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__属性之后重新分配该对象的__proto__属性。这将给您带来一些有趣的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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


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

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

他没有提到的是这个;

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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,而不使用它将分配返回值。


您可以使用它进行错误处理和调试,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
try{
    somefunction();
}
catch(error){
    if (error instanceof TypeError) {
        // Handle type Error
    } else if (error instanceof ReferenceError) {
        // Handle ReferenceError
    } else {
        // Handle all other error types
    }
}

1
2
3
4
5
6
7
8
9
10
//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);


What is it?

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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

    What problems does it solve?

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

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    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()函数中的原型是否位于对象上。之后,选择适当的方法来执行。不执行此检查可能会导致执行不存在的方法,从而导致引用错误。

    When is it appropriate and when not?

    我们已经讨论过了。当你需要检查一个对象的原型时,可以在使用它之前使用它。


    instanceof只是isPrototypeOf的语法糖:

    4

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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再次只是…的语法甜头,但这是另一回事。


    关于"什么时候合适,什么时候不合适?"我的2分钱:

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


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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

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

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