判斷js數據類型的四種方法和原理

怎麼去判斷一個數據屬於哪個數據類型,這個是很常見的操作,我們一般都會想到typeof和instanceof這兩個常見的方法,但有時候這兩種方法並不能滿足我們的需求。那讓我們來看一下都有哪些可以判斷數據類型的方法吧。

1.typeof
這個方法很常見,一般用來判斷基本數據類型,如:string,number,boolean,symbol,bigint(es10新增一種基本數據類型bigint,詳細介紹),undefined等。
typeof 目前能返回string,number,boolean,symbol,bigint,unfined,object,function這八種判斷類型

   typeof '123'     //  string
   typeof 1        //  number
   typeof true     //  boolean
   typeof Symbol('1')   // symbol
   typeof 111n    //  bigint
   typeof undefined        // undefined
   typeof null     // object
   typeof {a:1,b:2}    // object
   function c(){console.log('123')}
   typeof c     //  function

其實我一直有個疑問,null既然屬於基本數據類型,爲什麼用typeof返回的是object呢?解答如下:
js 在底層存儲變量的時候,會在變量的機器碼的低位1-3位存儲其類型信息?
000:對象
010:浮點數
100:字符串
110:布爾
1:整數
但是對於 undefined 和 null 來說,這兩個值的信息存儲是有點特殊的。
null:所有機器碼均爲0
undefined:用 −2^30 整數來表示
所以,typeof 在判斷 null 的時候就出現問題了,由於 null 的所有機器碼均爲0,因此直接被當做了對象來看待。

2.instanceof
一般用來判斷引用數據類型的判斷,如:Object,Function,Array,Date,RegExp等

        /s/g instanceof RegExp 
        // true
        new Date('2019/01/05') instanceof Date
        // true
        [1,2,3] instanceof Array
        // true

instanceof 主要的作用就是判斷一個實例是否屬於某種類型,例如:

let animal = function () {
}
let monkey = new animal()
monkey instanceof animal   // true

當然,instanceof 也可以判斷一個實例是否是其父類型或者祖先類型的實例。

let person = function () {
}
let programmer = function () {
}
programmer.prototype = new person()
let nicole = new programmer()
nicole instanceof person    // true
nicole instanceof programmer   // true

但是 instanceof 的原理是什麼呢?可以用下面的代碼解釋一下:

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

3、constructor
當一個函數F被定義時,JS引擎會爲F添加prototype原型,然後再在prototype上添加一個constructor屬性,並讓其指向F的引用。如下所示:
在這裏插入圖片描述
當執行 var f = new F() 時,F被當成了構造函數,f是F的實例對象,此時F原型上的constructor傳遞到了f上,因此f.constructor == F
在這裏插入圖片描述
可以看出,JS在函數F的原型上定義了constructor,當F被當作構造函數用來創建對象時,創建的新對象就被標記爲了“F” 類型,使得新對象有名有姓,可以追溯。

同理,JS中的數據類型也遵守這個規則:
在這裏插入圖片描述
細節問題:
1.null和undefined是無效的對象,因此是不會有constructor存在的,這兩種類型的數據可以通過第四種方法來判斷。
2.JS對象的constructor是不穩定的,這個主要體現在自定義對象上,當開發者重寫prototype後,原有的constructor會丟失,constructor會默認爲Object
在這裏插入圖片描述

4、Object.prototype.toString(這個是判斷類型最準的方法)
toString是Object原型對象上的一個方法,該方法默認返回其調用者的具體類型,更嚴格的講,是 toString運行時this指向的對象類型, 返回的類型格式爲[object,xxx],xxx是具體的數據類型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,… 基本上所有對象的類型都可以通過這個方法獲取到。

Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window是全局對象global的引用

需要注意的是,必須通過Object.prototype.toString.call來獲取,而不能直接 new Date().toString(), 從原型鏈的角度講,所有對象的原型鏈最終都指向了Object, 按照JS變量查找規則,其他對象應該也可以直接訪問到Object的toString方法,而事實上,大部分的對象都實現了自身的toString方法,這樣就可能會導致Object的toString被終止查找,因此要用call來強制執行Object的toString方法。

最後非常感謝這兩篇博客的幫助
判斷JS數據類型的四種方法
淺談 instanceof 和 typeof 的實現原理

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