查漏補(bǔ)缺!JS中數(shù)據(jù)類型的判斷
面試季馬上就要過(guò)去了,很多小伙伴也是在努力的抓住金九銀十的小尾巴,今天來(lái)說(shuō)一說(shuō)面試中常見(jiàn)的一個(gè)問(wèn)題:js中如何判斷數(shù)據(jù)類型?
眾所周知,JavaScript是一門弱類型的語(yǔ)言,它允許不兼容的類型進(jìn)行運(yùn)算,但有些場(chǎng)景使得我們不得不對(duì)數(shù)據(jù)類型做一個(gè)規(guī)范,如此就需要對(duì)數(shù)據(jù)類型進(jìn)行判斷。
可能你會(huì)回答,我一般用typeof來(lái)進(jìn)行判斷,可是面試官會(huì)問(wèn)你,那如果遇到值為null的情況下typeof會(huì)返回什么?對(duì)于初入江湖的新手來(lái)說(shuō)可能在這個(gè)時(shí)候就敗下陣來(lái),沒(méi)錯(cuò),我當(dāng)初也是這樣,判斷數(shù)據(jù)為null或者undefined不是用if-else來(lái)判斷的嗎?
帶著疑問(wèn),接下來(lái)我們就來(lái)看一下如何判斷數(shù)據(jù)類型:

typeof
在此引用MDN的說(shuō)明:
typeof 操作符返回一個(gè)字符串,表示未經(jīng)計(jì)算的操作數(shù)的類型。
下表總結(jié)了?typeof?可能的返回值:

示例:
console.log(typeof?333);?????????????//?numberconsole.log(typeof?true);??????????? //?booleanconsole.log(typeof?'aaa');???????????//?stringconsole.log(typeof []); // object []數(shù)組的數(shù)據(jù)類型在 typeof 中被解釋為 objectconsole.log(typeof function(){}); // functionconsole.log(typeof {}); // objectconsole.log(typeof undefined); // undefinedconsole.log(typeof null); // object null 的數(shù)據(jù)類型被 typeof 解釋為 object
null// JavaScript 誕生以來(lái)便如此typeof null === 'object';
在 JavaScript 最初的實(shí)現(xiàn)中,JavaScript 中的值是由一個(gè)表示類型的標(biāo)簽和實(shí)際數(shù)據(jù)值表示的。對(duì)象的類型標(biāo)簽是 0。由于?null?代表的是空指針(大多數(shù)平臺(tái)下值為 0x00),因此,null 的類型標(biāo)簽是 0,typeof null?也因此返回?"object"。
曾有一個(gè) ECMAScript 的修復(fù)提案(通過(guò)選擇性加入的方式),但被拒絕了。該提案會(huì)導(dǎo)致?typeof null === 'null'。
在 ECMAScript 2015 之前,typeof?總能保證對(duì)任何所給的操作數(shù)返回一個(gè)字符串。即便是沒(méi)有聲明的標(biāo)識(shí)符,typeof?也能返回?'undefined'。使用?typeof?永遠(yuǎn)不會(huì)拋出錯(cuò)誤。
但在加入了塊級(jí)作用域的?let?和?const?之后,在其被聲明之前對(duì)塊中的?let?和?const?變量使用?typeof?會(huì)拋出一個(gè)?ReferenceError。塊作用域變量在塊的頭部處于“暫存死區(qū)”,直至其被初始化,在這期間,訪問(wèn)變量將會(huì)引發(fā)錯(cuò)誤。
typeof undeclaredVariable === 'undefined';typeof newLetVariable; // ReferenceErrortypeof newConstVariable; // ReferenceErrortypeof newClass; // ReferenceErrorlet newLetVariable;const newConstVariable = 'hello';class newClass{};

instanceof
依然引用MDN的說(shuō)明:
instanceof 運(yùn)算符用于檢測(cè)構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在某個(gè)實(shí)例對(duì)象的原型鏈上。
示例:
console.log(333?instanceof?Number);??????????????????//?falseconsole.log(true instanceof Boolean); // falseconsole.log('aaa'?instanceof?String);????????????????//?false??console.log([] instanceof Array); // trueconsole.log(function(){} instanceof Function); // trueconsole.log({} instanceof Object); // true
以上結(jié)果顯示,直接的字面量值判斷數(shù)據(jù)類型,只有引用數(shù)據(jù)類型(Array,F(xiàn)unction,Object)被精準(zhǔn)判斷,其他(Number,Boolean,String)字面值不能被instanceof精準(zhǔn)判斷。
需要注意的是,如果表達(dá)式?obj instanceof Foo?返回?true,則并不意味著該表達(dá)式會(huì)永遠(yuǎn)返回?true,因?yàn)?Foo.prototype?屬性的值有可能會(huì)改變,改變之后的值很有可能不存在于?obj?的原型鏈上,這時(shí)原表達(dá)式的值就會(huì)成為?false。另外一種情況下,原表達(dá)式的值也會(huì)改變,就是改變對(duì)象?obj?的原型鏈的情況,雖然在目前的ES規(guī)范中,我們只能讀取對(duì)象的原型而不能改變它,但借助于非標(biāo)準(zhǔn)的?__proto__?偽屬性,是可以實(shí)現(xiàn)的。比如執(zhí)行?obj.__proto__ = {}?之后,obj instanceof Foo?就會(huì)返回?false?了。
到這里大家可能已經(jīng)觀察到了,為什么我在上面的示例中沒(méi)有用instanceof來(lái)判斷null和undefined,我們?cè)跒g覽器中輸出一下看是什么結(jié)果:

瀏覽器在這里報(bào)錯(cuò)了,它認(rèn)為null,undefined不是構(gòu)造器。

Object.prototype.toString.call()
最后,留到最后的才是最好的,我們來(lái)看一下終極大招:
Object.prototype.toString.call()
MDN中說(shuō)明:
toString()?方法返回一個(gè)表示該對(duì)象的字符串。
每個(gè)對(duì)象都有一個(gè)?toString()?方法,當(dāng)該對(duì)象被表示為一個(gè)文本值時(shí),或者一個(gè)對(duì)象以預(yù)期的字符串方式引用時(shí)自動(dòng)調(diào)用。默認(rèn)情況下,toString()?方法被每個(gè)?Object?對(duì)象繼承。如果此方法在自定義對(duì)象中未被覆蓋,toString()?返回?"[object?type]",其中?type?是對(duì)象的類型。以下代碼說(shuō)明了這一點(diǎn):
var o = new Object();o.toString(); // returns [object Object]
可以通過(guò)?toString()?來(lái)獲取每個(gè)對(duì)象的類型。為了每個(gè)對(duì)象都能通過(guò)?Object.prototype.toString()?來(lái)檢測(cè),需要以?Function.prototype.call()?或者?Function.prototype.apply()?的形式來(lái)調(diào)用,傳遞要檢查的對(duì)象作為第一個(gè)參數(shù),稱為?thisArg。
var toString = Object.prototype.toString;toString.call(333); // [object Number]toString.call("aaa");????????//?[object String]toString.call(true);?????????// [object Boolean]toString.call([]);???????????// [object Array]toString.call(function(){});?//?[object Function]toString.call({});???????????//?[object?Object]toString.call(undefined);????//?[object?Undefined]toString.call(null); // [object Null]// 甚至于js的內(nèi)置對(duì)象也能被精確判斷toString.call(new?Date);?????//?[object?Date]toString.call(new String); // [object String]toString.call(Math); // [object Math]
相信到這里你已經(jīng)知道該如何回答面試官的問(wèn)題了吧
