怎麼去判斷一個數據屬於哪個數據類型,這個是很常見的操作,我們一般都會想到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 的實現原理