js類型判斷及實現原理

typeof 實現原理

typeof 一般被用於判斷如number, string, object, boolean, function, undefined, symbol 這些類型,在非object類型數據時,type of返回的結果比較清楚。但是 在判斷object類型數據的時候只能告訴我們這個數據的類型是 object,而不能告訴我們具體是哪一種object

那麼,typeof是如何判斷這些類型的呢?其實是直接去判斷了js底層存儲變量類型的信息:

在 js 的最初版本中,使用的是 32 位系統,爲了性能考慮使用低位存儲了變量的類型信息,會在變量的機器碼的低位1-3位存儲其類型信息,如:

  • 000:對象
  • 010:浮點數
  • 100:字符串
  • 110:布爾
  • 1:整數

對於 undefinednull 來說,這兩個值的信息存儲是有點特殊的:

  • null:所有機器碼均爲0
  • undefined:用 −2^30 整數來表示

所以一個js的遺留bug原因我們也知道了,就是 typeof 在判斷 null 的時候出現問題,由於 null 的所有機器碼均爲0,因此直接被當做了對象來看待

因此,在使用typeof判斷類型時,最好使用用來判斷基本數據類型,避免對null進行判斷,object由於不夠準確,也不推薦使用。

Object.prototype.toString.call()

如果我們想獲得一個變量的正確類型,可以通過 Object.prototype.toString.call(xx)。這樣我們就可以獲得類似 [object Type] 的字符串

Object.prototype.toString.call(1) // "[object Number]"

Object.prototype.toString.call('hi') // "[object String]"

Object.prototype.toString.call({a:'hi'}) // "[object Object]"

Object.prototype.toString.call([1,'a']) // "[object Array]"

Object.prototype.toString.call(true) // "[object Boolean]"

Object.prototype.toString.call(() => {}) // "[object Function]"

Object.prototype.toString.call(null) // "[object Null]"

Object.prototype.toString.call(undefined) // "[object Undefined]"

Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"

instanceof 實現原理

其實 instanceof 主要的實現原理就是隻要右邊變量的 prototype 在左邊變量的原型鏈上即可。因此,instanceof 在查找的過程中會遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype,如果查找失敗,則會返回 false,告訴我們左邊變量並非是右邊變量的實例。

大致原理用一段代碼表示爲

function new_instance_of(leftVaule, rightVaule) { 
    let rightProto = rightVaule.prototype; // 取右表達式的 prototype 值
    leftVaule = leftVaule.__proto__; // 取左表達式的__proto__值
    while (true) {
    	if (leftVaule === null) {
            return false;	
        }
        if (leftVaule === rightProto) {
            return true;	
        } 
        leftVaule = leftVaule.__proto__ 
    }
}

要全部瞭解instanceof實現原理,還需要知道 JavaScript 的原型繼承原理

在這裏插入圖片描述

我們知道每個 JavaScript 對象均有一個隱式的 __proto__ 原型屬性,而顯式的原型屬性是 prototype,只有 Object.prototype.__proto__ 屬性在未修改的情況下爲 null 值。根據圖上的原理,我們來梳理上面提到的幾個有趣的 instanceof 使用的例子。

  • Object instanceof Object

由圖可知,Object 的 prototype 屬性是 Object.prototype, 而由於 Object 本身是一個函數,由 Function 所創建,所以 Object.proto 的值是 Function.prototype,而 Function.prototype 的 proto 屬性是 Object.prototype,所以我們可以判斷出,Object instanceof Object 的結果是 true 。用代碼簡單的表示一下

leftValue = Object.__proto__ = Function.prototype;
rightValue = Object.prototype;
// 第一次判斷
leftValue != rightValue
leftValue = Function.prototype.__proto__ = Object.prototype
// 第二次判斷
leftValue === rightValue
// 返回 true
  • Foo instanceof Foo

Foo 函數的 prototype 屬性是 Foo.prototype,而 Foo 的 proto 屬性是 Function.prototype,由圖可知,Foo 的原型鏈上並沒有 Foo.prototype ,因此 Foo instanceof Foo 也就返回 false 。

leftValue = Foo, rightValue = Foo
leftValue = Foo.__proto = Function.prototype
rightValue = Foo.prototype
// 第一次判斷
leftValue != rightValue
leftValue = Function.prototype.__proto__ = Object.prototype
// 第二次判斷
leftValue != rightValue
leftValue = Object.prototype = null
// 第三次判斷
leftValue === null
// 返回 false
  • Foo instanceof Object
leftValue = Foo, rightValue = Object
leftValue = Foo.__proto__ = Function.prototype
rightValue = Object.prototype
// 第一次判斷
leftValue != rightValue
leftValue = Function.prototype.__proto__ = Object.prototype
// 第二次判斷
leftValue === rightValue
// 返回 true 
  • Foo instanceof Function
leftValue = Foo, rightValue = Function
leftValue = Foo.__proto__ = Function.prototype
rightValue = Function.prototype
// 第一次判斷
leftValue === rightValue
// 返回 true 

總結

簡單來說,我們使用 typeof 來判斷基本數據類型是 ok 的,不過需要注意當用 typeof 來判斷 null 類型時的問題,如果想要判斷一個對象的具體類型可以考慮用 instanceof,但是 instanceof 也可能判斷不準確,比如一個數組,他可以被 instanceof 判斷爲 Object。所以我們要想比較準確的判斷對象實例的類型時,可以採取 Object.prototype.toString.call 方法

參考文章:https://juejin.im/post/5b0b9b9051882515773ae714

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章