Operator instanceof
memungkinkan untuk memeriksa apakah suatu objek termasuk dalam kelas tertentu. Ini juga memperhitungkan warisan.
Pemeriksaan seperti itu mungkin diperlukan dalam banyak kasus. Misalnya, ini dapat digunakan untuk membangun fungsi polimorfik , yang memperlakukan argumen secara berbeda bergantung pada tipenya.
Sintaksnya adalah:
objek instance dari Kelas
Ia mengembalikan true
jika obj
milik Class
atau kelas yang mewarisinya.
Misalnya:
kelas Kelinci {} biarkan kelinci = Kelinci baru(); // apakah itu objek kelas Rabbit? alert( kelinci instanceof Kelinci ); // BENAR
Ia juga bekerja dengan fungsi konstruktor:
// bukannya kelas fungsi Kelinci() {} waspada( Kelinci baru() contoh Kelinci ); // BENAR
…Dan dengan kelas bawaan seperti Array
:
misalkan arr = [1, 2, 3]; peringatan( arr instanceof Array ); // BENAR peringatan( arr instanceof Objek ); // BENAR
Harap dicatat bahwa arr
juga termasuk dalam kelas Object
. Itu karena Array
secara prototipikal mewarisi dari Object
.
Biasanya, instanceof
memeriksa rantai prototipe untuk pemeriksaan. Kita juga dapat mengatur logika khusus dalam metode statis Symbol.hasInstance
.
Algoritma obj instanceof Class
bekerja kira-kira sebagai berikut:
Jika ada metode statis Symbol.hasInstance
, panggil saja: Class[Symbol.hasInstance](obj)
. Seharusnya mengembalikan true
atau false
, dan kita selesai. Begitulah cara kita menyesuaikan perilaku instanceof
.
Misalnya:
// setup instanceOf check yang mengasumsikan hal itu // apa pun yang memiliki properti canEat adalah binatang kelas Hewan { statis [Simbol.hasInstance](obj) { if (obj.canEat) mengembalikan nilai true; } } misalkan obj = { canEat: true }; alert(obj instanceof Animal); // benar: Animal[Symbol.hasInstance](obj) dipanggil
Kebanyakan kelas tidak memiliki Symbol.hasInstance
. Dalam hal ini, logika standar digunakan: obj instanceOf Class
memeriksa apakah Class.prototype
sama dengan salah satu prototipe dalam rantai prototipe obj
.
Dengan kata lain, bandingkan satu demi satu:
obj.__proto__ === Kelas.prototipe? obj.__proto__.__proto__ === Kelas.prototipe? obj.__proto__.__proto__.__proto__ === Kelas.prototipe? ... // jika ada jawaban yang benar, kembalikan benar // jika tidak, jika kita mencapai akhir rantai, kembalikan false
Pada contoh di atas rabbit.__proto__ === Rabbit.prototype
, sehingga langsung memberikan jawabannya.
Dalam hal pewarisan, kecocokannya terjadi pada langkah kedua:
kelas Hewan {} kelas Kelinci memperluas Hewan {} biarkan kelinci = Kelinci baru(); alert(contoh kelinci dari Hewan); // BENAR // kelinci.__proto__ === Animal.prototype (tidak cocok) // kelinci.__proto__.__proto__ === Hewan.prototipe (cocok!)
Berikut ilustrasi perbandingan rabbit instanceof Animal
dengan Animal.prototype
:
Ngomong-ngomong, ada juga metode objA.isPrototypeOf(objB), yang mengembalikan true
jika objA
ada di suatu tempat dalam rantai prototipe untuk objB
. Jadi pengujian obj instanceof Class
dapat diubah menjadi Class.prototype.isPrototypeOf(obj)
.
Ini lucu, tetapi konstruktor Class
sendiri tidak berpartisipasi dalam pemeriksaan! Hanya rantai prototipe dan Class.prototype
yang penting.
Hal ini dapat menimbulkan konsekuensi menarik ketika properti prototype
diubah setelah objek dibuat.
Seperti di sini:
fungsi Kelinci() {} biarkan kelinci = Kelinci baru(); // mengubah prototipe Kelinci.prototipe = {}; // ...bukan kelinci lagi! alert( kelinci instanceof Kelinci ); // PALSU
Kita sudah tahu bahwa objek biasa diubah menjadi string sebagai [object Object]
:
misalkan obj = {}; peringatan(keberatan); // [Objek Objek] peringatan(obj.toString()); // sama
Itulah implementasi toString
mereka. Namun ada fitur tersembunyi yang membuat toString
sebenarnya jauh lebih hebat dari itu. Kita dapat menggunakannya sebagai perluasan typeof
dan alternatif untuk instanceof
.
Kedengarannya aneh? Memang. Mari kita demistifikasi.
Berdasarkan spesifikasi, toString
bawaan dapat diekstraksi dari objek dan dieksekusi dalam konteks nilai lainnya. Dan hasilnya tergantung pada nilai tersebut.
Untuk nomor, itu akan menjadi [object Number]
Untuk boolean, itu akan menjadi [object Boolean]
Untuk null
: [object Null]
Untuk undefined
: [object Undefined]
Untuk array: [object Array]
…dll (dapat disesuaikan).
Mari kita tunjukkan:
// salin metode toString ke dalam variabel untuk kenyamanan biarkan objectToString = Objek.prototipe.toString; // tipe apa ini? biarkan arr = []; peringatan(objekToString.panggilan(arr) ); // [Array objek]
Di sini kami menggunakan panggilan seperti yang dijelaskan dalam bab Dekorator dan penerusan, panggilan/terapkan untuk menjalankan fungsi objectToString
dalam konteks this=arr
.
Secara internal, algoritma toString
memeriksa this
dan mengembalikan hasil yang sesuai. Contoh lainnya:
let s = Objek.prototipe.toString; waspada( s.panggilan(123) ); // [Nomor objek] peringatan( s.panggilan(null) ); // [objek Null] waspada( s.panggilan(peringatan) ); // [Fungsi objek]
Perilaku Objek toString
dapat dikustomisasi menggunakan properti objek khusus Symbol.toStringTag
.
Misalnya:
biarkan pengguna = { [Symbol.toStringTag]: "Pengguna" }; peringatan( {}.toString.panggilan(pengguna) ); // [Pengguna objek]
Untuk sebagian besar objek spesifik lingkungan, terdapat properti seperti itu. Berikut adalah beberapa contoh spesifik browser:
// toStringTag untuk objek dan kelas khusus lingkungan: peringatan( jendela[Simbol.toStringTag]); // Jendela peringatan( XMLHttpRequest.prototype[Simbol.toStringTag] ); // Permintaan XMLHttp peringatan( {}.toString.call(jendela) ); // [Jendela objek] peringatan( {}.toString.call(XMLHttpRequest()) ); // [objek XMLHttpRequest]
Seperti yang Anda lihat, hasilnya persis Symbol.toStringTag
(jika ada), dimasukkan ke dalam [object ...]
.
Pada akhirnya kami memiliki "typeof on steroid" yang tidak hanya berfungsi untuk tipe data primitif, tetapi juga untuk objek bawaan dan bahkan dapat dikustomisasi.
Kita bisa menggunakan {}.toString.call
alih-alih instanceof
untuk objek bawaan ketika kita ingin mendapatkan tipe sebagai string, bukan sekadar memeriksa.
Mari kita rangkum metode pengecekan tipe yang kita ketahui:
bekerja untuk | kembali | |
---|---|---|
typeof | primitif | rangkaian |
{}.toString | primitif, objek bawaan, objek dengan Symbol.toStringTag | rangkaian |
instanceof | objek | benar/salah |
Seperti yang bisa kita lihat, {}.toString
secara teknis merupakan typeof
yang “lebih canggih”.
Dan operator instanceof
benar-benar bersinar ketika kita bekerja dengan hierarki kelas dan ingin memeriksa kelas dengan mempertimbangkan warisan.
pentingnya: 5
Pada kode di bawah ini, mengapa instanceof
mengembalikan true
? Kita dapat dengan mudah melihat bahwa a
tidak dibuat oleh B()
.
fungsi A() {} fungsi B() {} A.prototipe = B.prototipe = {}; misalkan a = baru A(); peringatan( turunan dari B ); // BENAR
Ya, memang terlihat aneh.
Tapi instanceof
tidak peduli dengan fungsinya, melainkan prototype
nya, yang cocok dengan rantai prototype.
Dan di sini a.__proto__ == B.prototype
, jadi instanceof
mengembalikan true
.
Jadi, berdasarkan logika instanceof
, prototype
sebenarnya mendefinisikan tipe, bukan fungsi konstruktor.