Apa instance dari operator dalam JavaScript?

313

Kata instanceofkunci dalam JavaScript bisa sangat membingungkan ketika pertama kali ditemukan, karena orang cenderung berpikir bahwa JavaScript bukan bahasa pemrograman berorientasi objek.

  • Apa itu?
  • Masalah apa yang dipecahkan?
  • Kapan itu tepat dan kapan tidak?
Alon Gubkin
sumber
3
Meskipun jawaban di bawah ini sangat berguna, Anda tidak menggunakannya terlalu banyak (setidaknya saya tidak) dalam aplikasi kata nyata. Seseorang akan membuat properti object.type yang memegang string dan memeriksa.
Omar Al-Ithawi
4
JS tidak masuk akal sama sekali: "foo" instanceof String=> false, 1 instanceof Number=> false, {} instanceof Object=> false. Katakan apa?!
morbusg
12
@morbusg komentar Anda menyesatkan. Pertama "foo" instanceof String => falsebenar, karena typeof "foo" == 'string'. new String("foo") instanceof String => true, karena typeof String == 'function'- Anda harus memperlakukan fungsi seperti kelas (definisi kelas). Variabel menjadi instanceofbeberapa function(kelas) saat Anda menetapkannya sebagai var v = new AnythingWhatTypeofEqualsFunction(). Hal yang sama berlaku untuk 1. typeof 1 == 'number'- 'number' is not 'function' :) Selanjutnya - {} instanceof Objectada TRUEdi node dan browser modern
fider
1
@fider: Itu komentar terhadap spek bahasa, berasal dari rubyist.
morbusg
@morbusg - ({}) instanceof Objectakan kembali true. Bahkan kode yang Anda tulis akan memberi Anda kesalahan.
DDM

Jawaban:

279

contoh

Operan Sisi Tangan Kiri (LHS) adalah objek aktual yang sedang diuji ke operan Sisi Tangan Kanan (RHS) yang merupakan konstruktor aktual dari suatu kelas. Definisi dasarnya adalah:

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

Berikut adalah beberapa contoh bagus dan berikut adalah contoh yang diambil langsung dari situs pengembang 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)

Satu hal yang layak disebutkan adalah nilai instanceoftrue jika objek mewarisi dari prototipe kelas:

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

Itu p instanceof Personbenar karena pmewarisi dari Person.prototype.

Sesuai permintaan OP

Saya telah menambahkan contoh kecil dengan beberapa kode sampel dan penjelasan.

Ketika Anda mendeklarasikan variabel, Anda memberinya jenis tertentu.

Misalnya:

int i;
float f;
Customer c;

Di atas menunjukkan beberapa variabel, yaitu i, f, dan c. Jenisnya adalah integer, floatdan Customertipe data yang ditentukan pengguna . Jenis seperti di atas bisa untuk bahasa apa saja, bukan hanya JavaScript. Namun, dengan JavaScript saat Anda mendeklarasikan variabel yang tidak Anda tentukan jenisnya secara eksplisit var x,, x bisa berupa angka / string / tipe data yang ditentukan pengguna. Jadi apa yang instanceofdilakukannya adalah memeriksa objek untuk melihat apakah itu dari tipe yang ditentukan sehingga dari atas mengambil Customerobjek yang bisa kita lakukan:

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!

Di atas kita telah melihat yang cdideklarasikan dengan tipe Customer. Kami sudah baru dan memeriksa apakah itu tipe Customeratau tidak. Tentu, itu mengembalikan benar. Kemudian masih menggunakan Customerobjek yang kita periksa apakah itu a String. Tidak, jelas bukan Stringkita yang baru saja meluncurkan Customerobjek, bukan Stringobjek. Dalam hal ini, ia mengembalikan false.

Sesederhana itu!

JonH
sumber
@Alon - Saya menambahkan contoh untuk Anda. Lihat di atas, color1 adalah tipe string, jadi ketika Anda mengatakan color1 instanceof String;ini akan mengembalikan true karena color1 adalah string.
JonH
@Alon - Ini memeriksa objek untuk melihat jenis objek apa itu. Pertimbangkan objek orang / pelanggan. Jadi person p = new person()p sekarang tipe orang dan bukan tipe string.
JonH
Dalam JavaScript, mungkin sulit untuk memahami bahwa suatu variabel memiliki fleksibilitas untuk memiliki tipe yang berbeda sepanjang rentang hidupnya. Contoh kode: jsfiddle.net/sikusikucom/znSPv
moey
@ Siku-Siku.Com - Saya tidak melihat trik untuk itu, var zero = 0; alert(zero); zero = "0"; alert(zero)kami beralih dari primitif intke primitif stringtanpa masalah.
JonH
Oh, maksudku itu sulit / baru untuk memahami (tidak melakukan ) bahwa "fleksibilitas" dalam JavaScript, esp. untuk beberapa yang terbiasa dengan bahasa yang membutuhkan mis casting eksplisit untuk mengubah tipe variabel. Seperti yang Anda tunjukkan, sangat mudah untuk mengubah jenis dari misalnya primitif intke primitif string.
moey
95

Ada sisi penting dari contoh yang tampaknya tidak tercakup dalam salah satu komentar sejauh ini: warisan. Variabel yang sedang dievaluasi dengan menggunakan instanceof dapat mengembalikan true untuk beberapa "jenis" karena pewarisan prototypal.

Misalnya, mari kita tentukan jenis dan subtipe:

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;

Sekarang kita memiliki beberapa "kelas" yang memungkinkan kita membuat beberapa instance, dan mencari tahu apa yang mereka contoh:

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

Lihat baris terakhir itu? Semua panggilan "baru" ke fungsi mengembalikan objek yang diwarisi dari Objek. Ini berlaku bahkan ketika menggunakan singkatan pembuatan objek:

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

Dan bagaimana dengan definisi "kelas" itu sendiri? Apa contohnya?

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

Saya merasa bahwa memahami bahwa objek apa pun dapat menjadi turunan dari jenis GANDA adalah penting, karena Anda saya (salah) menganggap bahwa Anda dapat membedakan antara, katakan dan objek dan fungsi dengan menggunakan instanceof. Seperti contoh terakhir ini jelas menunjukkan fungsi adalah objek.

Ini juga penting jika Anda menggunakan pola pewarisan apa pun dan ingin mengonfirmasi keturunan objek dengan metode selain mengetik bebek.

Semoga itu bisa membantu siapa pun menjelajah instanceof .

webnesto
sumber
Yang lebih hebat lagi adalah bahwa setelah Anda mewarisi dari prototipe dengan SubFoo.prototype = new Foo();Anda dapat menambahkan lebih banyak metode untuk itu, dan subfoo instanceof Foocek masih akan lulus jugasubfoo instanceof SubFoo
Cory Danielson
87

Jawaban lain di sini benar, tetapi mereka tidak masuk ke dalam bagaimana instanceofsebenarnya bekerja, yang mungkin menarik bagi beberapa pengacara bahasa di luar sana.

Setiap objek dalam JavaScript memiliki prototipe, yang dapat diakses melalui __proto__properti. Fungsi juga memiliki prototypeproperti, yang merupakan inisial __proto__untuk objek apa pun yang dibuat olehnya. Ketika suatu fungsi dibuat, itu diberikan objek unik untuk prototype. The instanceofOperator menggunakan keunikan ini untuk memberikan jawaban. Inilah yang instanceofmungkin terlihat jika Anda menulisnya sebagai fungsi.

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

Ini pada dasarnya memparafrasekan ECMA-262 edisi 5.1 (juga dikenal sebagai ES5), bagian 15.3.5.3.

Perhatikan bahwa Anda dapat menetapkan ulang objek apa pun ke properti fungsi prototype, dan Anda dapat menetapkan ulang __proto__properti objek setelah dibangun. Ini akan memberi Anda beberapa hasil menarik:

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
Jay Conrod
sumber
5
Perlu dicatat bahwa manipulasi langsung dari __proto__properti " " tidak diperbolehkan di IE. Jika saya ingat dengan benar, manipulasi langsung dari properti juga tidak termasuk dalam spesifikasi ECMA sehingga mungkin merupakan ide yang buruk untuk menggunakannya untuk tugas selain dalam kegiatan akademik.
webnesto
@webnesto, itu benar, proto tidak ada dalam spec. Saya tidak tahu IE tidak mendukungnya. Ketika Anda mengatakan manipulasi langsung, maksud Anda itu tidak terkena kode JS sama sekali, atau hanya tidak bisa ditulisi?
Jay Conrod
2
Tidak 100% yakin pada versi yang lebih lama. Kedengarannya seperti dari sini ( stackoverflow.com/questions/8413505/proto-for-ie9-or-ie10 ) bahwa di IE9 setidaknya dapat dibaca (meskipun tidak bisa berubah). Juga patut dicatat bahwa sepertinya browser mungkin menjatuhkannya sepenuhnya.
webnesto
4
Memahami keberadaan tersirat dari properti proto adalah penting apakah itu dapat diakses oleh kode pengguna atau tidak. +10 jika saya bisa mengutip spec, inilah yang saya cari di sini.
Mike Edwards
3
Untuk mendapatkan tautan prototipe, gunakan Object.getPrototypeOf(o), ini akan sama dengan yang __proto__Anda jelaskan, tetapi sesuai dengan ECMAScript.
froginvasion
45

Saya pikir perlu dicatat bahwa instanceof ditentukan oleh penggunaan kata kunci "baru" ketika mendeklarasikan objek. Dalam contoh dari 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)

Yang tidak dia sebutkan adalah ini;

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

Menentukan "baru" sebenarnya menyalin kondisi akhir fungsi konstruktor String ke dalam color1 var, bukan hanya mengaturnya ke nilai kembali. Saya pikir ini lebih baik menunjukkan apa kata kunci baru;

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'

Menggunakan "baru" memberikan nilai "ini" di dalam fungsi ke var yang dideklarasikan, sementara tidak menggunakannya memberikan nilai kembali sebagai gantinya.

Stephen Belanger
sumber
2
Itu tidak masuk akal untuk digunakan newdengan semua jenis JavaScript yang membuat jawaban yang diterima jauh lebih membingungkan bagi pemula. text = String('test')dan options = {}tidak akan diuji dengan instanceoftetapi, sebaliknya, dengan typeofsebaliknya.
Ryan
3
Date.getTime () // gagal. ya, baru itu penting.
Stephen Belanger
1
Coba jalankan itu di konsol. Anda akan mendapatkan kesalahan karena getTime () tidak ada. Anda harus menggunakan yang baru.
Stephen Belanger
8

Dan Anda dapat menggunakannya untuk penanganan kesalahan dan debugging, seperti ini:

try{
    somefunction();
} 
catch(error){
    if (error instanceof TypeError) {
        // Handle type Error
    } else if (error instanceof ReferenceError) {
        // Handle ReferenceError
    } else {
        // Handle all other error types
    }
}
Tarek Saied
sumber
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);
yfeldblum
sumber
3

Apa itu?

Javascript adalah bahasa prototipe yang artinya menggunakan prototipe untuk 'warisan'. yang instanceofOperator tes jika konstruktor fungsi ini prototypepropertype hadir dalam __proto__rantai obyek. Ini berarti bahwa ia akan melakukan hal berikut (dengan asumsi testObj adalah objek fungsi):

obj instanceof testObj;
  1. Periksa apakah prototipe objek sama dengan prototipe konstruktor: obj.__proto__ === testObj.prototype >> jika ini true instanceofakan kembalitrue .
  2. Akan memanjat rantai prototipe. Sebagai contoh: obj.__proto__.__proto__ === testObj.prototype >> jika ini true instanceofakan kembalitrue .
  3. Akan mengulangi langkah 2 hingga prototipe penuh objek diperiksa. Jika tidak ada rantai prototipe objek yang cocok testObj.prototypemaka instanceofoperator akan kembali false.

Contoh:

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

Masalah apa yang dipecahkan?

Itu memecahkan masalah dengan mudah memeriksa apakah suatu objek berasal dari prototipe tertentu. Misalnya, ketika suatu fungsi menerima objek yang dapat memiliki berbagai prototipe. Kemudian, sebelum menggunakan metode dari rantai prototipe, kita bisa menggunakaninstanceof operator untuk memeriksa apakah metode ini ada di objek.

Contoh:

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

Di sini, di talk()fungsi pertama diperiksa jika prototipe terletak pada objek. Setelah ini, metode yang sesuai diambil untuk dieksekusi. Tidak melakukan pemeriksaan ini dapat mengakibatkan mengeksekusi metode yang tidak ada dan dengan demikian kesalahan referensi.

Kapan itu tepat dan kapan tidak?

Kami agak sudah membahas ini. Gunakan saat Anda perlu memeriksa prototipe objek sebelum melakukan sesuatu dengannya.

Willem van der Veen
sumber
1
Saya pikir Anda ingin menulis sesuatu yang lain selain " prototipe fungsi konstruktor muncul di suatu tempat di properti prototipe konstruktor "
Bergi
Anda mungkin ingin menyebutkan bahwa akan lebih tepat untuk meminta orang-orang berbagi antarmuka dan memberi nama kedua metode tersebut PersonX.prototype.talk, sehingga talkfungsi tersebut bisa dilakukan person.talk().
Bergi
Anda benar-benar memperbaruinya, jadi definisi ini lebih baik. Terima kasih telah menunjukkan!
Willem van der Veen
Juga, jangan gunakan __proto__dalam dokumentasi, itu sudah usang - tulis Object.getPrototype()saja
Bergi
1

Pada pertanyaan "Kapan itu tepat dan kapan tidak?", 2 sen saya:

instanceofjarang berguna dalam kode produksi, tetapi berguna dalam pengujian di mana Anda ingin menegaskan bahwa kode Anda mengembalikan / membuat objek dari jenis yang benar. Dengan secara eksplisit tentang jenis objek yang dikembalikan / dibuat oleh kode Anda, pengujian Anda menjadi lebih kuat sebagai alat untuk memahami dan mendokumentasikan kode Anda.

Andrew Magee
sumber
1

instanceofhanyalah gula sintaksis untuk 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 hanya tergantung pada prototipe konstruktor suatu objek.

Konstruktor hanyalah fungsi normal. Sebenarnya itu adalah objek fungsi, karena semuanya adalah objek dalam Javascript. Dan objek fungsi ini memiliki prototipe, karena setiap fungsi memiliki prototipe.

Prototipe hanyalah objek normal, yang terletak di dalam rantai prototipe objek lain. Itu berarti berada dalam rantai prototipe objek lain membuat objek prototipe:

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

The instanceofOperator harus dihindari karena palsu kelas, yang tidak ada di Javascript. Meskipun classkata kunci tidak dalam ES2015, karena classlagi-lagi hanya gula sintaksis untuk ... tapi itu cerita lain.


sumber
1
Baris pertama salah! Lihat di sini untuk lebih lanjut: "isPrototypeOf () berbeda dari instanceof operator. Dalam ungkapan" instance instanceof Fungsi ", rantai prototipe objek diperiksa terhadap AFunction.prototype, bukan terhadap AFunction itu sendiri."
Yan Foto
1
@ Ya Dan masih instanceofberasal dari isPrototypeOf. Saya menyebutnya gula sintaksis. Dan sumber MDN Anda adalah lelucon, bukan?
Tidak. Itu tidak dan tidak seharusnya menjadi lelucon, tetapi masih merupakan tautan yang salah. Saya dimaksudkan untuk memiliki satu ini diposting. Saya tidak ingin masuk ke masalah teknis, tetapi instanceof tidak melakukan hal yang sama isPrototypeOf. Itu dia.
Yan Foto
0

Saya baru saja menemukan aplikasi dunia nyata dan akan menggunakannya lebih sering sekarang, saya pikir.

Jika Anda menggunakan acara jQuery, kadang-kadang Anda ingin menulis fungsi yang lebih umum yang juga dapat dipanggil langsung (tanpa acara). Anda dapat menggunakan instanceofuntuk memeriksa apakah parameter pertama dari fungsi Anda adalah instance dari jQuery.Eventdan bereaksi dengan tepat.

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

Dalam kasus saya, fungsi yang diperlukan untuk menghitung sesuatu baik untuk semua (melalui acara klik pada tombol) atau hanya satu elemen tertentu. Kode yang saya gunakan:

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