instanceof使用中可能漏掉的一點細節

前言

在面嚮對象語言中,通常都有關鍵字 instanceof 來檢測對象類型,更準確點來講是檢測對象是哪一個類型的實例。那麼在 JS 中這個關鍵字又有什麼不一樣之處呢?此文僅是一篇對 ES 標準中 instanceof 關鍵字的解讀,並記錄了在此過程當中的對 JS 中對象系統的一點小感悟。javascript

標準規定該關鍵字須要作什麼

先舉例 a instanceof b, 下文中的步驟解釋中關鍵字左側的值用a表示, 右側的值用b表示。java

在標準中 instanceof 的行爲被抽象到 InstanceofOperator 操做中,下面列出比較關鍵的兩個抽象操做,並根據本身的理解對整理了下步驟:編程

  • InstanceofOperator ( a, b);
  • OrdinaryHasInstance ( b, a );

InstanceofOperator ( a, b)

  1. 檢測b是不是對象,否TypeError
  2. 獲取b是否認義了Symbol.hasInstance
  3. 是則b[Symbol.hasInstance](a),返回true或false
  4. 不然判斷b是不是函數,不然TypeError
  5. 是則返回OrdinaryHasInstance(b, a)的運行結果

OrdinaryHasInstance ( b, a )

  1. 判斷b是不是函數,不然返回false(這一步在這一場景中應該不會被調用)
  2. 是則判斷b是不是被bind包裝過的函數,是則獲取到bind包裝的函數bc,並調用InstanceofOperator( a, bc)
  3. 是則檢測a是不是對象,不然返回false
  4. 是則獲取b的prototype屬性bp
  5. 若是bp不是對象則返回TypeError
  6. 獲取a的原型並賦值給a
  7. 檢測a是否爲null,是則返回false
  8. 不然判斷a和bp是否相等,是則返回true
  9. 不然重複步驟11~14直至返回true或false

幾處細節的推敲

在查看標準解釋是,有幾處理解的不是很明白,故使用幾個demo對本身的理解作了驗證函數

b應該是什麼值

先看b不是對象是什麼狀況 ui

從上面的結果能夠看到報錯了,並且錯誤提示很明顯的提示右側的值不是對象,這是第1步檢測報的錯,那麼若是是對象,而不是函數對象又會是什麼狀況呢?this

嗯,沒錯,對象也報錯了,此次的錯誤是右側的值不可調用,即不是函數,這應是第4步檢測報的錯了。根據上面的步驟試試給對象設置Symbol.hasInstance屬性spa

這一次沒有報錯,檢測正常進行了,且返回了在函數中定義的結果,根據上面的步驟和代碼驗證,能夠得出右側的值能夠是設置了Symbol.hasInstance屬性的對象。且檢測的結果會被右側值定義的Symbol.hasInsyance函數攔截成函數返回的結果。

右側是函數的狀況就是比覺正常的返回了,須要明確的一點是函數是不能直接設置Symbol.hasInstance屬性的,具體緣由,感興趣的能夠繼續查閱資料。到這裏能夠得出結論,b必須是設置了Symbol的對象或者函數,不然會報錯。prototype

被 dind 包裝過的函數依然會返回原函數原型檢測結果

這一步存疑的緣由是,沒有直接理解標準中的描述,只是有所猜想,不能肯定,故驗證之。code

var a = function() {
   this.name = 'a';
}; 
var b = {testName: 'b'};
a.prototype = b;

var c = new a();
console.log('c instanceof a', c instanceof a);   // true

var h = {name: 'h'};
var s = a.bind(h);
console.log('c instanceof s', c instanceof s);   // true
複製代碼

代碼傳送門cdn

如上代碼示例,從其運行結果可知,a.prototype 是等於 c 的原型的,s 是 a 被 bind 對象 h 後返回的函數,從驗證結果來看,函數通過 bind 的一層包裝,依然不會影響其原型檢測的綁定。

一點思考

在推敲上面細節時忽然聯想到這種檢測實際是很合理的,類比 Java 中的檢測類,這裏對象的原型不就能夠類比成 Java 裏面的類麼。 某個對象的原型是什麼決定它具有了原型對象的特性的,而類不也是這樣麼,該對象屬於哪一個類,決定了該對象具有了哪些特性。不一樣的是,在JS中的檢測是往繼承鏈上多退了一步的,Java 中是直接檢測對象是不是該類的實例,而JS中是檢測的是對象的原型是否等於函數的prototype屬性,這應該就是繼承原理不一樣產生的區別吧。

以前從未認真的思考過,JS 中的整個對象系統是什麼樣的,雖然如今也仍是沒有理解太多,可是至少如今在個人認知裏 JS 的 對象系統是簡單而完善的,並無由於簡單而缺乏了面向對象基本特性。雖然它靈活的容許你隨便更改一個對象的原型,也就是類,可是這依然不妨礙它成爲一門能夠面像對象編程的語言。

結論

instanceof 關鍵字檢測的本質是檢測左側對象的原型鏈上是否存在和右側函數的prototype屬性相等的對象,若是存在則返回true,若是不存在則返回false。

相關文章
相關標籤/搜索