Toán tử instanceof trong JavaScript là gì?


313

Các instanceof từ khóa trong JavaScript có thể khá khó hiểu khi nó bắt gặp đầu tiên, khi mọi người có xu hướng nghĩ rằng JavaScript không phải là một ngôn ngữ lập trình hướng đối tượng.

  • Nó là gì?
  • Nó giải quyết vấn đề gì?
  • Khi nào thì thích hợp và khi nào thì không?

3
Mặc dù các câu trả lời dưới đây rất hữu ích, nhưng bạn không sử dụng nó nhiều (ít nhất là tôi không) trong các ứng dụng từ thực. Người ta sẽ tạo một thuộc tính object.type chứa một chuỗi và kiểm tra nó.
Omar Al-Ithawi

4
JS không có ý nghĩa gì: "foo" instanceof String=> false, 1 instanceof Number=> false, {} instanceof Object=> false. Nói gì cơ?!
morbusg

12
@morbusg bình luận của bạn là sai lệch. Đầu tiên "foo" instanceof String => falselà chính xác, bởi vì typeof "foo" == 'string'. new String("foo") instanceof String => true, bởi vì typeof String == 'function'- bạn nên coi hàm như lớp (định nghĩa của lớp). Biến trở thành instanceofmột số function(lớp) khi bạn gán nó là var v = new AnythingWhatTypeofEqualsFunction(). Áp dụng tương tự cho 1. typeof 1 == 'number'- 'số' không phải là 'chức năng' :) Tiếp theo - {} instanceof Objectnằm TRUEtrong nút và các trình duyệt hiện đại
fider 12/12/13

1
@fider: Đó là bình luận về thông số ngôn ngữ, đến từ một người theo chủ nghĩa ruby.
morbusg

@morbusg - ({}) instanceof Objectsẽ trở lại true. Trong thực tế, mã bạn đã viết sẽ cung cấp cho bạn một lỗi.
DDM

Câu trả lời:


279

ví dụ

Toán hạng bên tay trái (LHS) là đối tượng thực tế đang được thử nghiệm với toán hạng bên tay phải (RHS) là hàm tạo thực tế của một lớp. Định nghĩa cơ bản là:

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

Dưới đây là một số ví dụ hay và đây là một ví dụ được lấy trực tiếp từ trang web dành cho nhà phát triển của Mozilla :

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)

Một điều đáng nói là được instanceofđánh giá là đúng nếu đối tượng kế thừa từ nguyên mẫu của classe:

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

Đó là p instanceof Personsự thật vì pkế thừa từ Person.prototype.

Theo yêu cầu của OP

Tôi đã thêm một ví dụ nhỏ với một số mã mẫu và giải thích.

Khi bạn khai báo một biến bạn cung cấp cho nó một loại cụ thể.

Ví dụ:

int i;
float f;
Customer c;

Trên đây chỉ cho bạn một số biến, cụ thể là i, f, và c. Các loại là integer, floatvà một Customerkiểu dữ liệu do người dùng xác định . Các loại như ở trên có thể dành cho bất kỳ ngôn ngữ nào, không chỉ JavaScript. Tuy nhiên, với JavaScript khi bạn khai báo một biến bạn không xác định rõ ràng một loại var x, x có thể là một số / chuỗi / kiểu dữ liệu do người dùng xác định. Vì vậy, những gì instanceofnó kiểm tra đối tượng để xem nếu nó thuộc loại được chỉ định để từ trên lấy Customerđối tượng chúng ta có thể làm:

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!

Ở trên chúng tôi đã thấy rằng cđã được tuyên bố với các loại Customer. Chúng tôi đã mới và kiểm tra xem nó có phải là loại Customerhay không. Chắc chắn là, nó trả về đúng sự thật. Sau đó vẫn sử dụng Customerđối tượng chúng ta kiểm tra nếu nó là a String. Không, chắc chắn không phải là Stringchúng tôi mới một Customerđối tượng không phải là một Stringđối tượng. Trong trường hợp này, nó trả về false.

Nó thật sự đơn giản!


@Alon - Tôi đã thêm một ví dụ cho bạn. Xem ở trên, color1 là kiểu chuỗi, vì vậy khi bạn nói color1 instanceof String;điều này sẽ trả về true vì color1 là một chuỗi.
JonH

@Alon - Nó kiểm tra một đối tượng để xem nó là loại đối tượng nào. Hãy xem xét một người / đối tượng khách hàng. Vì vậy, person p = new person()p bây giờ là một loại người và không phải là một loại chuỗi.
JonH

Trong JavaScript, có thể khó hiểu rằng một biến có tính linh hoạt để có một loại khác trong suốt vòng đời của nó. Ví dụ mã: jsfiddle.net/sikusikucom/znSPv
moey

@ Siku-Siku.Com - Tôi không thấy bất kỳ mẹo nào cho nó, var zero = 0; alert(zero); zero = "0"; alert(zero)chúng tôi đã đi từ nguyên thủy intsang nguyên thủy stringmà không có vấn đề gì.
JonH

Ồ, ý tôi là thật khó hiểu / chưa hiểu (không làm ) rằng "tính linh hoạt" trong JavaScript, đặc biệt. đối với một số người đã quen với một ngôn ngữ yêu cầu ví dụ như đúc rõ ràng để thay đổi loại biến. Như bạn đã chỉ ra, rất dễ dàng để thay đổi loại từ ví dụ nguyên thủy intsang nguyên thủy string.
moey

95

Có một khía cạnh quan trọng đối với cá thể mà dường như không được đề cập trong bất kỳ ý kiến ​​nào cho đến nay: sự kế thừa. Một biến được đánh giá bằng cách sử dụng instanceof có thể trả về true cho nhiều "loại" do kế thừa nguyên mẫu.

Ví dụ: hãy xác định một loại và một kiểu con:

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;

Bây giờ chúng ta có một vài "lớp" cho phép tạo một số trường hợp và tìm hiểu xem chúng là trường hợp nào:

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

Xem dòng cuối cùng? Tất cả các lệnh gọi "mới" tới hàm trả về một đối tượng kế thừa từ Object. Điều này đúng ngay cả khi sử dụng tốc ký tạo đối tượng:

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

Và những gì về định nghĩa "lớp"? Họ là những trường hợp của?

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

Tôi cảm thấy rằng việc hiểu rằng bất kỳ đối tượng nào cũng có thể là một thể hiện của các loại MULTIPLE là quan trọng, vì bạn (không chính xác) cho rằng bạn có thể phân biệt giữa, nói và đối tượng và một chức năng bằng cách sử dụng instanceof. Như ví dụ cuối cùng này cho thấy rõ ràng một chức năng một đối tượng.

Điều này cũng quan trọng nếu bạn đang sử dụng bất kỳ mẫu thừa kế nào và muốn xác nhận thế hệ con của một đối tượng bằng các phương thức khác ngoài gõ vịt.

Hy vọng rằng sẽ giúp bất cứ ai khám phá instanceof.


Điều tuyệt vời hơn nữa là sau khi bạn kế thừa từ nguyên mẫu, SubFoo.prototype = new Foo();bạn có thể thêm nhiều phương thức vào nó và subfoo instanceof Fookiểm tra vẫn sẽ vượt qua cũng nhưsubfoo instanceof SubFoo
Cory Danielson

87

Các câu trả lời khác ở đây là chính xác, nhưng chúng không đi vào cách thức instanceofthực sự hoạt động, có thể được một số luật sư ngôn ngữ ngoài kia quan tâm.

Mọi đối tượng trong JavaScript đều có một nguyên mẫu, có thể truy cập thông qua thuộc __proto__tính. Các hàm cũng có một thuộc prototypetính, là thuộc tính ban đầu __proto__cho bất kỳ đối tượng nào được tạo bởi chúng. Khi một chức năng được tạo ra, nó được cung cấp một đối tượng duy nhất cho prototype. Nhà instanceofđiều hành sử dụng tính duy nhất này để cung cấp cho bạn một câu trả lời. Đây là những gì instanceofcó thể trông giống như nếu bạn viết nó như là một chức năng.

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__;
  }
}

Về cơ bản, đây là phiên bản ECMA-262 phiên bản 5.1 (còn được gọi là ES5), phần 15.3.5.3.

Lưu ý rằng bạn có thể gán lại bất kỳ đối tượng nào cho thuộc tính của hàm prototypevà bạn có thể gán lại thuộc __proto__tính của đối tượng sau khi nó được xây dựng. Điều này sẽ cung cấp cho bạn một số kết quả thú vị:

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

5
Điều đáng chú ý là thao tác trực tiếp của thuộc tính " __proto__" không được phép trong IE. Nếu tôi nhớ lại một cách chính xác, thao tác trực tiếp của tài sản cũng không được bao gồm trong thông số ECMA vì vậy có lẽ đó là một ý tưởng tồi để sử dụng nó cho nhiệm vụ khác hơn là theo đuổi học thuật.
webnesto

@webnesto, đó là sự thật, proto không có trong thông số kỹ thuật. Tôi không biết IE đã không hỗ trợ nó mặc dù. Khi bạn nói thao tác trực tiếp, bạn có nghĩa là nó hoàn toàn không tiếp xúc với mã JS, hoặc nó chỉ không thể ghi được?
Jay Conrod

2
Không chắc chắn 100% trên các phiên bản cũ. Âm thanh giống như từ đây ( stackoverflow.com/questions/8413505/proto-for-ie9-or-ie10 ) rằng trong IE9, nó ít nhất có thể đọc được (mặc dù không thể thay đổi). Cũng đáng chú ý rằng có vẻ như các trình duyệt có thể bỏ nó hoàn toàn.
webnesto

4
Hiểu được sự tồn tại ngụ ý của thuộc tính proto rất quan trọng cho dù mã người dùng có thể truy cập được hay không. +10 nếu tôi có thể trích dẫn thông số kỹ thuật, đây chính xác là những gì tôi đến đây để tìm kiếm.
Mike Edwards

3
Để có được liên kết nguyên mẫu, hãy sử dụng Object.getPrototypeOf(o), nó sẽ giống như __proto__bạn mô tả, nhưng phù hợp với ECMAScript.
froginvasion

45

Tôi nghĩ rằng đáng chú ý rằng thể hiện đó được xác định bằng cách sử dụng từ khóa "mới" khi khai báo đối tượng. Trong ví dụ từ 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)

Những gì anh ấy đã không đề cập là điều này;

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

Chỉ định "mới" thực sự sao chép trạng thái kết thúc của hàm xây dựng chuỗi vào var color1, thay vì chỉ đặt nó thành giá trị trả về. Tôi nghĩ rằng điều này tốt hơn cho thấy những gì các từ khóa mới làm;

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'

Sử dụng "new" sẽ gán giá trị của "this" bên trong hàm cho var được khai báo, trong khi không sử dụng nó sẽ gán giá trị trả về thay thế.


2
Nó không có ý nghĩa để sử dụng newvới bất kỳ loại JavaScript nào, khiến câu trả lời được chấp nhận trở nên khó hiểu hơn nhiều đối với người mới bắt đầu. text = String('test')options = {}sẽ không được thử nghiệm bởi instanceofnhưng, thay typeofvào đó, thay vào đó.
Ryan

3
Date.getTime () // thất bại. vâng, mới là quan trọng.
Stephen Belanger

1
Hãy thử chạy nó trong một giao diện điều khiển. Bạn sẽ gặp lỗi vì getTime () không tồn tại. Bạn cần phải sử dụng mới.
Stephen Belanger

8

Và bạn có thể sử dụng nó để xử lý lỗi và gỡ lỗi, như thế này:

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

3
//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

Nó là gì?

Javascript là một ngôn ngữ nguyên mẫu có nghĩa là nó sử dụng các nguyên mẫu cho 'kế thừa'. các instanceofnhà điều hành kiểm tra nếu một hàm constructor của prototypepropertype hiện diện trong __proto__chuỗi của một đối tượng. Điều này có nghĩa là nó sẽ làm như sau (giả sử testObj là một đối tượng hàm):

obj instanceof testObj;
  1. Kiểm tra xem nguyên mẫu của đối tượng có bằng nguyên mẫu của hàm tạo không: obj.__proto__ === testObj.prototype >> nếu điều này true instanceofsẽ trả về true.
  2. Sẽ leo lên chuỗi nguyên mẫu. Ví dụ: obj.__proto__.__proto__ === testObj.prototype >> nếu điều này true instanceofsẽ trở lại true.
  3. Sẽ lặp lại bước 2 cho đến khi nguyên mẫu đầy đủ của đối tượng được kiểm tra. Nếu không có chuỗi nào trên chuỗi nguyên mẫu của đối tượng được khớp với testObj.prototypethì instanceoftoán tử sẽ trả về false.

Thí dụ:

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

Nó giải quyết vấn đề gì?

Nó giải quyết vấn đề kiểm tra thuận tiện nếu một đối tượng xuất phát từ một nguyên mẫu nhất định. Ví dụ, khi một hàm nhận một đối tượng có thể có các nguyên mẫu khác nhau. Sau đó, trước khi sử dụng các phương thức từ chuỗi nguyên mẫu, chúng ta có thể sử dụng instanceoftoán tử để kiểm tra xem các phương thức này có nằm trên đối tượng hay không.

Thí dụ:

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);

Ở đây trong talk()chức năng đầu tiên được kiểm tra nếu nguyên mẫu nằm trên đối tượng. Sau đó, phương thức thích hợp được chọn để thực thi. Không thực hiện kiểm tra này có thể dẫn đến việc thực thi một phương thức không tồn tại và do đó xảy ra lỗi tham chiếu.

Khi nào thì thích hợp và khi nào thì không?

Chúng tôi đã đi qua đây. Sử dụng nó khi bạn cần kiểm tra nguyên mẫu của một đối tượng trước khi làm điều gì đó với nó.


1
Tôi nghĩ rằng bạn muốn viết một cái gì đó ngoài " nguyên mẫu của hàm xây dựng xuất hiện ở đâu đó trong thuộc tính nguyên mẫu của hàm tạo "
Bergi

Bạn có thể muốn đề cập rằng sẽ phù hợp hơn khi yêu cầu mọi người chia sẻ một giao diện và đặt tên cho cả hai phương thức đó PersonX.prototype.talk, để talkchức năng có thể thực hiện một cách đơn giản person.talk().
Bergi

Bạn đã hoàn toàn đúng cập nhật nó, vì vậy định nghĩa là tốt hơn. Cảm ơn đã chỉ ra!
Willem van der Veen

Ngoài ra, không sử dụng __proto__trong tài liệu, nó không được dùng nữa - Object.getPrototype()thay vào đó hãy viết
Bergi

1

Về câu hỏi "Khi nào thì phù hợp và khi nào không?", 2 xu của tôi:

instanceofhiếm khi hữu ích trong mã sản xuất, nhưng hữu ích trong các thử nghiệm mà bạn muốn khẳng định rằng mã của bạn trả về / tạo các đối tượng của các loại chính xác. Bằng cách rõ ràng về các loại đối tượng mà mã của bạn đang trả về / tạo, các thử nghiệm của bạn trở nên mạnh mẽ hơn như một công cụ để hiểu và ghi lại mã của bạn.


1

instanceofchỉ là cú pháp đường cho 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 chỉ phụ thuộc vào nguyên mẫu của một constructor của một đối tượng.

Một constructor chỉ là một chức năng bình thường. Nói đúng ra nó là một đối tượng chức năng, vì mọi thứ đều là một đối tượng trong Javascript. Và đối tượng chức năng này có một nguyên mẫu, bởi vì mọi chức năng đều có một nguyên mẫu.

Một nguyên mẫu chỉ là một đối tượng bình thường, nằm trong chuỗi nguyên mẫu của một đối tượng khác. Điều đó có nghĩa là trong chuỗi nguyên mẫu của một đối tượng khác làm cho một đối tượng thành nguyên mẫu:

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

Các instanceofnhà điều hành nên tránh vì nó fakes lớp học, mà không tồn tại trong Javascript. Mặc dù classtừ khóa không có trong ES2015, nhưng classmột lần nữa chỉ là cú pháp cú pháp cho ... nhưng đó là một câu chuyện khác.


1
Dòng đầu tiên là không chính xác! Xem ở đây để biết thêm: "isPrototypeOf () khác với toán tử instanceof. Trong biểu thức" object instanceof AFeft ", chuỗi nguyên mẫu đối tượng được kiểm tra đối với AFeft.prototype, không chống lại chính AFeft."
Yan Foto

1
@Yan Và vẫn instanceofcó nguồn gốc từ isPrototypeOf. Tôi gọi đây là đường cú pháp. Và nguồn MDN của bạn là một trò đùa, phải không?

Không. Nó không phải và không được coi là một trò đùa, nhưng vẫn là liên kết sai. Tôi đã có ý định đăng cái này Tôi không muốn đi sâu vào kỹ thuật, nhưnginstanceof không làm điều tương tự như isPrototypeOf. Đó là nó.
Yan Foto

0

Tôi chỉ tìm thấy một ứng dụng trong thế giới thực và sẽ sử dụng nó thường xuyên hơn bây giờ, tôi nghĩ vậy.

Nếu bạn sử dụng các sự kiện jQuery, đôi khi bạn muốn viết một hàm chung hơn cũng có thể được gọi trực tiếp (không có sự kiện). Bạn có thể sử dụng instanceofđể kiểm tra xem tham số đầu tiên của hàm có phải là một thể hiện jQuery.Eventvà phản ứng thích hợp không.

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

Trong trường hợp của tôi, hàm cần thiết để tính toán một cái gì đó cho tất cả (thông qua sự kiện nhấp chuột trên một nút) hoặc chỉ một yếu tố cụ thể. Mã tôi đã sử dụng:

var recalc = function (el) { 
    el = (el == undefined || el instanceof $.Event) ? $('span.allItems') : $(el);
    // calculate...
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.