W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
?instanceof
? 操作符用于檢查一個(gè)對(duì)象是否屬于某個(gè)特定的 class。同時(shí),它還考慮了繼承。
在許多情況下,可能都需要進(jìn)行此類檢查。例如,它可以被用來(lái)構(gòu)建一個(gè) 多態(tài)性(polymorphic) 的函數(shù),該函數(shù)根據(jù)參數(shù)的類型對(duì)參數(shù)進(jìn)行不同的處理。
語(yǔ)法:
obj instanceof Class
如果 obj
隸屬于 Class
類(或 Class
類的衍生類),則返回 true
。
例如:
class Rabbit {}
let rabbit = new Rabbit();
// rabbit 是 Rabbit class 的對(duì)象嗎?
alert( rabbit instanceof Rabbit ); // true
它還可以與構(gòu)造函數(shù)一起使用:
// 這里是構(gòu)造函數(shù),而不是 class
function Rabbit() {}
alert( new Rabbit() instanceof Rabbit ); // true
……與諸如 Array
之類的內(nèi)建 class 一起使用:
let arr = [1, 2, 3];
alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true
有一點(diǎn)需要留意,arr
同時(shí)還隸屬于 Object
類。因?yàn)閺脑蜕蟻?lái)講,Array
是繼承自 Object
的。
通常,instanceof
在檢查中會(huì)將原型鏈考慮在內(nèi)。此外,我們還可以在靜態(tài)方法 Symbol.hasInstance
中設(shè)置自定義邏輯。
obj instanceof Class
算法的執(zhí)行過(guò)程大致如下:
Symbol.hasInstance
?,那就直接調(diào)用這個(gè)方法:例如:
// 設(shè)置 instanceOf 檢查
// 并假設(shè)具有 canEat 屬性的都是 animal
class Animal {
static [Symbol.hasInstance](obj) {
if (obj.canEat) return true;
}
}
let obj = { canEat: true };
alert(obj instanceof Animal); // true:Animal[Symbol.hasInstance](obj) 被調(diào)用
Symbol.hasInstance
?。在這種情況下,標(biāo)準(zhǔn)的邏輯是:使用 ?obj instanceOf Class
? 檢查 ?Class.prototype
? 是否等于 ?obj
? 的原型鏈中的原型之一。換句話說(shuō)就是,一個(gè)接一個(gè)地比較:
obj.__proto__ === Class.prototype?
obj.__proto__.__proto__ === Class.prototype?
obj.__proto__.__proto__.__proto__ === Class.prototype?
...
// 如果任意一個(gè)的答案為 true,則返回 true
// 否則,如果我們已經(jīng)檢查到了原型鏈的尾端,則返回 false
在上面那個(gè)例子中,rabbit.__proto__ === Rabbit.prototype
,所以立即就給出了結(jié)果。
而在繼承的例子中,匹配將在第二步進(jìn)行:
class Animal {}
class Rabbit extends Animal {}
let rabbit = new Rabbit();
alert(rabbit instanceof Animal); // true
// rabbit.__proto__ === Animal.prototype(無(wú)匹配)
// rabbit.__proto__.__proto__ === Animal.prototype(匹配?。?/code>
下圖展示了 rabbit instanceof Animal
的執(zhí)行過(guò)程中,Animal.prototype
是如何參與比較的:
這里還要提到一個(gè)方法 objA.isPrototypeOf(objB),如果 objA
處在 objB
的原型鏈中,則返回 true
。所以,可以將 obj instanceof Class
檢查改為 Class.prototype.isPrototypeOf(obj)
。
這很有趣,但是 Class
的 constructor 自身是不參與檢查的!檢查過(guò)程只和原型鏈以及 Class.prototype
有關(guān)。
創(chuàng)建對(duì)象后,如果更改 prototype
屬性,可能會(huì)導(dǎo)致有趣的結(jié)果。
就像這樣:
function Rabbit() {}
let rabbit = new Rabbit();
// 修改了 prototype
Rabbit.prototype = {};
// ...再也不是 rabbit 了!
alert( rabbit instanceof Rabbit ); // false
大家都知道,一個(gè)普通對(duì)象被轉(zhuǎn)化為字符串時(shí)為 [object Object]
:
let obj = {};
alert(obj); // [object Object]
alert(obj.toString()); // 同上
這是通過(guò) toString
方法實(shí)現(xiàn)的。但是這兒有一個(gè)隱藏的功能,該功能可以使 toString
實(shí)際上比這更強(qiáng)大。我們可以將其作為 typeof
的增強(qiáng)版或者 instanceof
的替代方法來(lái)使用。
聽起來(lái)挺不可思議?那是自然,精彩馬上揭曉。
按照 規(guī)范 所講,內(nèi)建的 toString
方法可以被從對(duì)象中提取出來(lái),并在任何其他值的上下文中執(zhí)行。其結(jié)果取決于該值。
[object Number]
?[object Boolean]
?null
?:?[object Null]
?undefined
?:?[object Undefined]
?[object Array]
?讓我們演示一下:
// 方便起見,將 toString 方法復(fù)制到一個(gè)變量中
let objectToString = Object.prototype.toString;
// 它是什么類型的?
let arr = [];
alert( objectToString.call(arr) ); // [object Array]
這里我們用到了在 裝飾器模式和轉(zhuǎn)發(fā),call/apply 一章中講過(guò)的 call 方法來(lái)在上下文 this=arr
中執(zhí)行函數(shù) objectToString
。
在內(nèi)部,toString
的算法會(huì)檢查 this
,并返回相應(yīng)的結(jié)果。再舉幾個(gè)例子:
let s = Object.prototype.toString;
alert( s.call(123) ); // [object Number]
alert( s.call(null) ); // [object Null]
alert( s.call(alert) ); // [object Function]
可以使用特殊的對(duì)象屬性 Symbol.toStringTag
自定義對(duì)象的 toString
方法的行為。
例如:
let user = {
[Symbol.toStringTag]: "User"
};
alert( {}.toString.call(user) ); // [object User]
對(duì)于大多數(shù)特定于環(huán)境的對(duì)象,都有一個(gè)這樣的屬性。下面是一些特定于瀏覽器的示例:
// 特定于環(huán)境的對(duì)象和類的 toStringTag:
alert( window[Symbol.toStringTag]); // Window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest
alert( {}.toString.call(window) ); // [object Window]
alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]
正如我們所看到的,輸出結(jié)果恰好是 Symbol.toStringTag
(如果存在),只不過(guò)被包裹進(jìn)了 [object ...]
里。
這樣一來(lái),我們手頭上就有了個(gè)“磕了藥似的 typeof”,不僅能檢查原始數(shù)據(jù)類型,而且適用于內(nèi)建對(duì)象,更可貴的是還支持自定義。
所以,如果我們想要獲取內(nèi)建對(duì)象的類型,并希望把該信息以字符串的形式返回,而不只是檢查類型的話,我們可以用 {}.toString.call
替代 instanceof
。
讓我們總結(jié)一下我們知道的類型檢查方法:
用于 | 返回值 | |
---|---|---|
typeof
|
原始數(shù)據(jù)類型 | string |
{}.toString
|
原始數(shù)據(jù)類型,內(nèi)建對(duì)象,包含 Symbol.toStringTag 屬性的對(duì)象 |
string |
instanceof
|
對(duì)象 | true/false |
正如我們所看到的,從技術(shù)上講,{}.toString
是一種“更高級(jí)的” typeof
。
當(dāng)我們使用類的層次結(jié)構(gòu)(hierarchy),并想要對(duì)該類進(jìn)行檢查,同時(shí)還要考慮繼承時(shí),這種場(chǎng)景下 instanceof
操作符確實(shí)很出色。
在下面的代碼中,為什么 instanceof
會(huì)返回 true
?我們可以明顯看到,a
并不是通過(guò) B()
創(chuàng)建的。
function A() {}
function B() {}
A.prototype = B.prototype = {};
let a = new A();
alert( a instanceof B ); // true
是的,看起來(lái)確實(shí)很奇怪。
instanceof
并不關(guān)心函數(shù),而是關(guān)心函數(shù)的與原型鏈匹配的 prototype
。
并且,這里 a.__proto__ == B.prototype
,所以 instanceof
返回 true
。
總之,根據(jù) instanceof
的邏輯,真正決定類型的是 prototype
,而不是構(gòu)造函數(shù)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: