原生JS|數據類型檢測,並沒你想象的那麼簡單
HTML5學堂-碼匠:看上去,JavaScript中的數據類型檢測,並沒有什麼難度,但是……它包含了不少的知識,如果你只知道一個typeof的話,那很建議你讀讀這篇文章,加強一下~
最近一個關係很不錯的朋友在跳槽,面試一家大型知名互聯網公司的時候,面試官問了一個看上去“超級”基礎的問題:如何進行數據類型的檢測啊?
數據類型檢測方法
1 最為基礎的typeof
2 不可不知的instanceof
3 比instanceof更好的constructor
4 檢測值或表達式結果是否為NaN
5 易用的jQuery函數-isFunction、isArray等
6 高大上的原型方法 Object.prototype.toString
【1】typeof 基本數據類型的檢測
語法與範例
<script>
// 語法:typeof 被檢測的內容;
var num = 2;
console.log(typeof num);
</script>
基本解析
typeof是一個運算符,針對一個操作數(操作數可以是變量也可以是常量)進行運算,其返回值是一個字符串,返回值包括:"number"、"string"、"boolean"、 "undefined"、"object"、"function"。
<script>
var user;
var obj = {
user : 'HTML5學堂'
}
var h5course = function(){
console.log('碼匠');
}
console.log(typeof 123);
console.log(typeof 'html5');
console.log(typeof true);
console.log(typeof user);
console.log(typeof obj);
console.log(typeof h5course);
</script>
typeof的侷限性
typeof的問題在於:針對對象類型數據,無法進行具體細化的檢測。對於數組、正則、對象{}、null等數據,雖然均屬於對象類型,但卻各不相同,使用typeof進行檢測時,均返回object。
<script>
console.log(typeof null);
console.log(typeof {'user': 'HTML5學堂'});
console.log(typeof [4, 20]);
console.log(typeof (/\d{8}/));
</script>
【2】不可不知的instanceof
面對typeof的類型檢測缺陷,可以使用instanceof來彌補。
語法與範例
<script>
// 語法: 要檢測的內容 instanceof 類型
console.log([4,20] instanceof Array);
console.log(/\d{8}/ instanceof RegExp);
// 如上兩個返回值均為true
</script>
基本解析
instanceof,能夠用於數據類型的檢測,但是僅限於引用類型數據,無法檢測基本數據類型的值;檢測的返回值內容是布爾值。此外,會受到原型鏈的影響。
注:關於值類型與引用類型變量,如果不明白可以戳文章底部的相關文章鏈接進行查看。
instanceof的侷限性
侷限性1:不能夠檢測以“字面量”方式出現的基本數據類型;
<script>
// 侷限1 - 字面量式的基本數據類型無法檢測
var str = 'HTML5學堂';
var str2 = new String('碼匠');
console.log(str instanceof String);
// str的檢測返回false
console.log(str2 instanceof String);
// str2的檢測返回true
</script>
代碼解析:str是使用“字面量”的方式創建的字符串,而str2是使用String對象實例化的字符串。檢測str時,返回結果為false;而檢測str2時,返回結果為true。
侷限性2:會檢測該類所歸屬的原型鏈,只要在原型鏈當中能夠找到,檢測結果均為true,檢測結果有可能會出現問題。
<script>
// 範例1
var str = new String('碼匠');
console.log(str instanceof String);
console.log(str instanceof Object);
// 範例2
var box = document.getElementsByTagName('body');
console.log(box[0] instanceof HTMLBodyElement);
console.log(box[0] instanceof Node);
console.log(box[0] instanceof Object);
// 範例3
var arr = ['HTML5學堂', '碼匠'];
console.log(arr instanceof Array);
console.log(arr instanceof Object);
</script>
代碼解析:
在此處如果不是很瞭解原型鏈的童鞋,可以換個角度來理解。例如:數組(Array)隸屬於對象(Object),對於這種歸屬,檢測也是成功的。
還不理解?好,我們換一個更易懂的!人類,屬於哺乳動物,此時檢測我們是否是哺乳動物,結果會是什麼呢?
範例1中的字符串,的確屬於string類型,但是它是通過String構造函數實例化得到的,String本身是一個字符串對象,所以str也符合Object這個條件。
範例2中的body標籤本身是一個對象,細化一些說是一個“節點對象”,再細化說,是一個“body元素”,所以三種檢測均為true。範例3中的數組同理(數組屬於對象的一個分類)
【3】比instanceof更好的constructor
語法與範例
<script>
// 語法: 要檢測的內容.constructor === 類型
console.log([4, 20].constructor === Array);
console.log(/\d{8}/.constructor === RegExp);
console.log('碼匠'.constructor === String);
var num = 123;
console.log(num.constructor === Number);
// 如上幾個返回值均為true
</script>
基本解析
constructor是對象的一個屬性,不是運算符,constructor屬性指向對象的構造函數。constructor的作用與instanceof基本類似,但是它對instanceof的兩個缺陷均進行了彌補,也就是說:既能夠檢測基本數據類型,又不受到原型鏈的影響。
<script>
// 範例1
var box = document.getElementsByTagName('body');
console.log(box[0].constructor === HTMLBodyElement);
console.log(box[0].constructor === Node);
console.log(box[0].constructor === Object);
// 範例1中只有第一個返回true,後兩者均返回false
// 範例2
console.log([4, 20].constructor === Array);
console.log([4, 20].constructor === Object);
// 範例2中,Array的返回true,而Object的檢測返回false
</script>
constructor的侷限性
對於自己創建的構造函數,constructor的侷限性會比較大(當然這裡不是我們主要要討論的東西),constructor屬性是易變的,可以進行定義,所以並不能夠保證它指向相應的構造函數。但是,對於系統的各類構造函數,還是可以正常使用的,畢竟我們平日裡並不會去修改系統默認對象的constructor指向的。
<script>
function Me() {};
var demo = new Me();
console.log(demo.constructor);
// 此處檢測的結果是,constructor指向構造函數Me,但是我們卻可以人工修改指向,比如修改為Peo(如下代碼)
function Peo() {};
demo.constructor = Peo;
console.log(demo.constructor);
// 此處檢測的結果就變成了Peo
</script>
相關說明
如果希望瞭解instanceof、constructor的基本原理,需要掌握原型,瞭解構造函數的內在機制。如果在這方面積累不太夠的小夥伴,建議可以先掌握這些知識點,然後後期隨著自己知識的深入逐漸的理解實現原理。
檢測值或表達式結果是否為NaN
【4】isNaN函數
isNaN用於檢測值或表達式“轉換為數字”時,是否為NaN。可以用於輔助parseFloat()和parseInt()進行進一步的結果檢測。
<script>
console.log(isNaN(NaN));
console.log(isNaN(123+234));
console.log(isNaN('a1234'));
</script>
【5】易用的JQ函數-isArray等
jQuery當中,提供了大量的數據類型檢測方法(isArray、isFunction等等),可以檢查數據屬於哪種具體的對象類型,此處就不多談了,感興趣的查看JQ的API文檔即可。
【6】Object.prototype.toString.call()
語法與範例
<script>
console.log(Object.prototype.toString.call(123));
console.log(Object.prototype.toString.call(null));
console.log(Object.prototype.toString.call(true));
console.log(Object.prototype.toString.call('碼'));
console.log(Object.prototype.toString.call(/\d/));
console.log(Object.prototype.toString.call([4]));
// 如上的打印結果:
// [object Number]
// [object Null]
// [object Boolean]
// [object String]
// [object RegExp]
// [object Array]
</script>
基本解析
Object.prototype.toString比較常用於判斷對象值屬於哪種內置屬性,返回值類型為字符串,返回的字符串格式為:"[object 數據類型]"。由於許多引用類型都重寫了Object繼承來的toString方法,所以通常使用call/apply方法,借用Object.prototype.toString函數來判斷數據類型。
每一種數據類型所屬的類的原型上都有toString方法,例如:Number.prototype、String.prototype、Array.prototype等等。除了Object上的toString之外,其他類原型上的toString都用於將數據值轉換為字符串。
Plus
可以藉助字符串截取的方法,獲取Object.prototype.toString的結果,並進行處理,從而得到“Number”、“Null”等數據類型字符串,從而更方便進行數據類型比較/檢測。
<script>
var demo = '測試用data';
var type = Object.prototype.toString.call(demo).slice(8, -1);
console.log(type);
</script>