在 JavaScript 中,咱們一般用 typeof
判斷類型,可是在判斷引用類型的值時,經常會遇到一個問題:不管引用的是什麼類型的對象,都會返回 "object"(固然還有 "function") 。有時候咱們須要知道這個引用對象的類型是數組仍是一個包裝對象,這個時候 instanceof
就能夠派上用場了。git
廢話很少說,先來幾個例子熱身一下,所有都知道同窗,請點擊右上角的關閉按鈕;模模糊糊的同窗,能夠繼續閱讀,只要掌握了原理,這些題目真的是易如反掌。github
const a = 'abc'; console.log(a instanceof String); // ? const b = new String('abc'); console.log(b instanceof String); // ?
console.log(String instanceof String); // ? console.log(Object instanceof Object); // ? console.log(Function instanceof Function); // ? console.log(Function instanceof Object); // ?
在 MDN 上是這樣描述 instanceof
的:面試
instanceof
運算符用於測試構造函數的prototype
屬性是否出如今對象原型鏈中的任何位置
換句話說,若是A instanceof B
,那麼 A
必須是一個對象,而 B
必須是一個合法的 JavaScript 函數。在這兩個條件都知足的狀況下:數組
判斷 B 的 prototype 屬性指向的原型對象(B.prototype)是否在對象 A 的原型鏈上。若是在,則爲 true;若是不在,則爲 false。函數
下面咱們舉一個例子一步步來講明:學習
function Person() {} const p1 = new Person(); p1 instanceof Person; // true
第一步:每個構造函數都有一個 prototype
屬性。測試
第二步:這個 prototype
屬性指向這個構造函數的原型對象spa
第三步:經過 new
關鍵字,能夠建立一個構造函數的實例(這裏是 p1),而實例上都有一個 __proto__
屬性prototype
第四步:實例上的 __proto__
屬性也指向構造函數的原型對象,這樣咱們就能夠獲得一張完整的關係圖了code
第五步:p1 instanceof Person
,檢查 B(Person) 的 prototype
屬性指向的原型對象,是否在對象 A(p1) 的原型鏈上。
通過咱們的一步步分解,發現 B(Person) 的 prototype
所指向的原型對象確實在 A(p1) 的原型鏈上,因此咱們能夠肯定 p1 instanceof Person
必定是爲 true
的。
咱們再深刻一點會發現,不只僅 p1 instanceof Person
爲 true
,p1 instanceof Object
也爲 true
,這又是爲何呢?
其實,Person
的原型對象上也有一個 __proto__
屬性,而這個屬性指向 Object
的 prototype
屬性所指向的原型對象,咱們能夠在控制檯打印一下:
既然有這個關係,那咱們再完善一下上面的圖:
經過 Person
的例子,咱們知道構造函數 Object
上的 prototype
屬性會指向它的原型對象:
如今,咱們要判斷 p1 instanceof Object
的真假,還記得上面的定義麼?咱們再來一遍:
判斷 B 的 prototype 屬性指向的原型對象(B.prototype)是否在對象 A 的原型鏈上。若是在,則爲 true;若是不在,則爲 false。
此時,咱們發現 B(Object) 的 prototype
屬性所指向的原型對象依然在 A(p1) 的原型鏈上,因此結果爲 true
。
經過上面的例子咱們能夠知道,其實 instanceof
的原理很是簡單,就是一個查找原型鏈的過程,因此只要你理解了原型鏈的相關知識,理解 instanceof
的原理就不會再有問題了。這裏咱們稍微總結兩點與instanceof
有關的原型鏈知識:
__proto__
屬性,只有 Object.prototype.__proto__ === null
;prototype
屬性指向它的原型對象,而構造函數實例的 __proto__
屬性也指向該原型對象;看了上面的過程,其實也很容易給出 instanceof
的實現方式:
function instance_of(left, right) { const RP = right.prototype; // 構造函數的原型 while(true) { if (left === null) { return false; } if (left === RP) { // 必定要嚴格比較 return true; } left = left.__proto__; // 沿着原型鏈從新賦值 } }
有了上面的實現方法,咱們再解釋一下上面的例子:
function Person() {} const p1 = new Person(); p1 instanceof Object; // 用上面的代碼解釋它
第一次賦值
left = p1 right = Object RP = Object.prototype
第一次判斷
left !== null
而且 left !== RP
,繼續向上尋找 left
的原型鏈,準備新的賦值。
第二次賦值
left = p1.__proto__ = Person.prototype
第二次判斷
left !== null
而且 left !== RP
,繼續向上尋找 left
的原型鏈,準備新的賦值。
第三次賦值
left = p1.__proto__.__proto__ = Person.prototype.__proto__
第三次賦值
left !== null
,此時 left === RP
,返回 true
,函數執行完畢。
今天,咱們用一個例子,經過畫圖以及代碼實現兩個角度剖析了 instanceof
的實現原理,其實思路也很簡單,無非就是一個沿原型鏈向上查找的過程。但願你們能夠在之後的面試過程當中,再也不被" instanceof
的實現原理是什麼?"這樣的面試難住了。
固然,閱讀永遠都只是一種十分被動的學習方法,我仍是建議你能本身實踐一下。在文章的開頭有幾個例子,感興趣的同窗能夠挑選一個例子,本身經過畫圖以及代碼實現兩種方式再加深一遍理解,相信你會理解的更深入。
若是文章中錯誤或表述不嚴謹的地方,歡迎指正。
最後,文章會首先發布在個人 Github ,以及公衆號上,歡迎關注。