Javascript 由於各種各樣的原因,在判斷一個變量的數據類型方面一直存在着一些問題,其中最典型的問題恐怕就是 typeof null 會返回 object 了吧。因此在這里簡單的總結一下判斷數據類型時常見的陷阱,以及正確的處理姿勢。
javascript 數據類型
數據類型
這里先談一下 javascript 這門語言的數據類型。javascript 中有七種數據類型,其中有六種簡單數據類型,一種復雜數據類型。
六種簡單數據類型
- String
- Number
- Boolean
- Null
- Undefined
- Symbol (ECMAScript 6 新定義)
復雜數據類型
Object 是唯一的復雜數據類型。 Object Array Function 這些引用類型值最終都可以歸結為 Object 復雜數據類型。
各種陷阱
typeof 的陷阱
typeof 是用來檢測變量數據類型的操作符,對一個值使用 typeof 操作符可能會返回下列某個字符串
- "undefined" --- 如果這個值未定義
- "string" --- 如果這個值是字符串
- "boolean" --- 如果這個值是布爾類型值
- "number" --- 如果這個值是數值
- "object" --- 如果這個值是對象或者 null
- "function" --- 如果這個值是函數
1. Object 對象檢測的陷阱
function isObj(obj) {
if (typeof obj === 'object') {
return 'It is object';
}
return 'It is not object';
}
這個函數的本意是想檢測傳入的參數是否是 Object 對象。但是這個函數其實是非常不安全的。
比如
var a = [1, 2, 3];
isObj(a); // 'It is object'
var b = null;
isObj(b); // 'It is object'
這樣明顯是不對的,因為 typeof [] 和 typeof null 都是是會返回 'object'的。
2. Array 對象檢測的陷阱
typeof [] // 'object'
上面說到了對一個數組使用 typeof 操作符也是會返回 'object',因此 typeof 並不能判斷數組對象的類型
instanceof 的陷阱 與 基本包裝類型
1. instanceof
instanceof 運算符用來測試一個對象在其原型鏈中是否存在一個構造函數的 prototype 屬性。
2. 基本包裝類
《Javascript》高級程序設計 第五章第六節 基本包裝類型
javascript 為了方便操作基本類型值,ECMAscript 提供了3個特殊的引用類型:Boolean、Number 和 String。
每當讀取一個基本類型值的時候,后台就會創建一個對應的基本包裝類型的對象,從而讓我們能夠調用一些方法來操作這些數據。
var s1 = "some text";
var s2 = s1.substring(2);
上面的代碼中,先創建了一個字符串保存在了變量 s1,字符串當然是基本類型值。但是在下一行中我們又調用了 s1 的方法。我們知道基本類型值不是對象,理論上它不應該擁有方法(但它們確實有方法)。其實,為了讓我們實現這種直觀的操作,后台已經幫助我們完成了一系列的操作。當我們在第二行代碼中訪問 s1 變量時,訪問過程處於讀取模式,而在讀取模式中訪問字符串時,后台都會自動完成下列處理。
- 創建 String 類型的一個實例;
- 在實例上調用指定的方法;
- 銷毀這個實例。
可以將以上三個步驟想像成是執行了下列代碼
var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;
3. instanceof 判斷基本類型值的陷阱
上面提到基本包裝類,就是為了說明 instanceof 這個陷阱
var str = 'text';
str instanceof String; // false
本來我也是想當然的認為 str instanceof String 會使 str 變量處於讀取模式,自動建立基本包裝類。但是根據上述代碼所體現表象來看,instanceof 運算符是直接訪問的變量的原始值。
因此 instanceof 並不能用來判斷五種基本類型值
4. instanceof 判斷 Object類型的陷阱
這里先說一下,用 instanceof 判斷 Array 類型基本上是非常ok的
var arr = [1, 2, 3];
arr instanceof Array; // true
但是 instanceof 卻不能安全的判斷 Object 類型,因為 Array 構造函數是繼承自 Object 對象的,因此在 arr 變量上是可以訪問到 Object 的 prototype 屬性的。如下例所示:
var arr = [1, 2, 3];
arr instanceof Object; // true
// 會返回 true ,是因為 Object 構造函數的 prototype 屬性存在與 arr 這個數組實例的原型鏈上。
一個高效但危險的變量類型判斷方法
用對象的 constructor 來判斷對象類型
stack overflow 上有人做了實驗,說是目前運算最快的判斷變量類型的方式。
function cstor(variable) {
var cst = variable.constructor;
switch (cst) {
case Number:
return 'Number'
case String:
return 'String'
case Boolean:
return 'Boolean'
case Array:
return 'Array'
case Object:
return 'Object'
}
}
上面是一個判斷變量類型的方法,工作的非常高效完美。但是用 constructor 判斷變量類型有一個致命的缺陷,就是當檢測 null 或者 undefined 類型的 constructor 屬性時,js會報錯!
也就是說下面代碼會報錯!
cstor(null); // 若傳入 null 或者 undefined 作為參數時
// cstor 函數第一行就會報錯,因為 null 和 undefined 根本就沒有 constructor 屬性
因此我們在利用變量的 constructor 屬性來判斷變量類型時,必須要先保證變量有 不會是 null 或者 undefined。
改造以上函數如下:
function cstor(variable) {
if (variable === null || variable === undefined) {
return 'Null or Undefined';
}
var cst = variable.constructor;
switch (cst) {
case Number:
return 'Number'
case String:
return 'String'
case Boolean:
return 'Boolean'
case Array:
return 'Array'
case Object:
return 'Object'
}
}
所以說使用 constructor 來判斷對象類型時要無時無刻不伴隨着排除 null 和 undefined 的干擾,否則就會產生致命的問題,因此本人並不推薦。
正確判斷變量類型的姿勢
一個萬金油方法 Object.prototype.toString.call()
Object.prototype.toString.call(variable) 用這個方法來判斷變量類型目前是最可靠的了,它總能返回正確的值。
該方法返回 "[object type]", 其中type是對象類型。
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call(123); // "[object Number]"
Object.prototype.toString.call('123'); // "[object String]"
Object.prototype.toString.call(false); // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
String Boolean Number Undefined 四種基本類型的判斷
除了 Null 之外的這四種基本類型值,都可以用 typeof 操作符很好的進行判斷處理。
typeof 'abc' // "string"
typeof false // "boolean"
typeof 123 // "number"
typeof undefined // "undefined"
Null 類型的判斷
除了 Object.prototype.toString.call(null) 之外,目前最好的方法就是用 variable === null 全等來判斷了。
var a = null;
if (a === null) {
console.log('a is null');
}
// a is null
檢測變量是否是一個 Array 數組
typeof [] 會返回 object 因此明顯是不能夠用 typeof 操作符進行數組類型判斷的。目前常用的方法有以下幾種
1. Object.prototype.toString.call()
萬金油方法是一種。
2. ECMAscript5 新增 Array.isArray()
Array.isArray([]); // true
3. instanceof 運算符
instanceof 運算符用來測試一個對象在其原型鏈中是否存在一個構造函數的 prototype 屬性。
因此可以檢測一個對象的原型鏈中是否存在 Array 構造函數的 prototype 屬性來判斷是不是數組。
[] instanceof Array // true
檢測變量是否是一個 Object 對象
typeof 和 instanceof 都不能安全的判斷變量是否是 Object 對象。
目前判斷變量是否是對象的最安全的方法就只有 Object.prototype.toString.call() 了。
作者博客:pspgbhu http://www.cnblogs.com/pspgbhu/
作者 Github:https://github.com/pspgbhu
歡迎轉載,但請注明出處,謝謝!
