typeof
typeof 操作符返回一個字符串,表示未經計算的操作數的類型。(MDN)
typeof 可以用來檢測一個值的類型。
1. 表現
在 ES6 之前,typeof 在瀏覽器的表現是這樣的:
類型 | 結果 |
---|---|
Boolean | “boolean” |
String | “string” |
Number | “Number” |
Function | “function” |
undefined | “undefined” |
null | “object” |
數組 | “object” |
任意對象 | “object” |
typeof 233; // 輸出:"number"
typeof '嘎?'; // 輸出:"string"
typeof true; // 輸出:"boolean"
typeof undefined; // 輸出:"undefined"
var fn1 = function() {};
function fn2() {};
typeof fn1; // 輸出:"function"
typeof fn2; // 輸出:"function"
typeof null; // 輸出:"object"
typeof []; // 輸出:"object";
typeof ['9', '9', '6']; // 輸出:"object";
typeof {}; // 輸出:"object"
2. 為什么檢查 null 的類型返回 object
這是一個歷史遺留問題,JavaScript 從出現開始都是這個表現。
typeof null; // 輸出:"object"
原因是 null
表示為一個空指針,其內部表示類型的標簽和對象相同,所以會被設別為 object
。
有提案表示想要修復這個問題,使表現如下:
typeof null; // 輸出:"null"
但這個提案被拒絕了。
3. 為什么檢查數組類型返回 object
數組的本質是個對象,從數組的原型上觀察就可以發現。
同時可以通過 instanceof
檢測數組的原型鏈上是否有 Object。
Array instanceof Object; // 輸出:true
4. 由基礎對象構建的值也返回 object
事實上 typeof 只對字面量敏感。
var num = 1;
typeof num; // 輸出:"number"
如果采用構造函數的形式得到一個值:
var num = new Number(1);
typeof num; // 輸出:"object"
所以除了 Function
,構造出來的一個值,使用 typeof 檢測類型都會返回 object
。
var fn = new Function('console.log("我是特例!")');
typeof fn; // 輸出:"function"
5. 更精準的檢測類型
使用 Object.prototype.toString.call
,可以更精準的檢測類型。
Object.prototype.toString.call(1); // 輸出: [object Number]
Object.prototype.toString.call(false); // 輸出: [object Boolean]
Object.prototype.toString.call(null); // 輸出: [object Null]
Object.prototype.toString.call([1]); // 輸出: [object Array]
Object.prototype.toString.call({}); // 輸出: [object Object]
通過觀察結果可以看到,使用這個方式可以區別出數組、對象、null這些 typeof
無法區分的類型。
可是為什么要這樣用呢?不能直接調用一個值的 toString
嗎?
這涉及到了原型的問題,例如 Number
:
var number = 996;
console.log(number.__proto__.toString);
number
變量的 toString
方法其實就是 Number
的 prototype
屬性下的 toString
方法。
var number = 996;
console.log(number.__proto__.toString === Number.prototype.toString);
從這就可以看出進行 number.toString()
操作,調用的就不是 Object.prototype.toString
了。
這兩個 toString
方法的內容不同,Number.prototype.toString
做的事情其實就是根據一些規則,將值轉成字符串,而 Object.prototype.toString
是將對象的一個類型標簽進行組合輸出。
也就是說大部分數據類型的原始對象都提供了新的 toString
方法,也就無法調用到 Object.prototype.toString
,所以要用這種方式。
那為什么 Object.prototype.toString
會可以精準判斷出一個值的類型呢?
這是因為每個值都有一個對應的類型標簽,在標準中為 [[class]]
。
在 ES6
中,則是使用Symbol.toStringTag
作為標記。
Object.prototype.toString
在調用的時候,就會訪問這個標記,并返回 [object 標記]
。
var obj = {
[Symbol.toStringTag]: '996',
};
Object.prototype.toString.call(obj); // 輸出:"[object 996]"
所有內置的類型都具有這個標記,所以使用 Object.prototype.toString.call(值)
的方式可以更精準的獲取到值的類型。
一些舊的數據類型的基礎對象為了兼容性,可能訪問不到 Symbol.toStringTag
接口,但是其他許多內置對象可以,例如JSON
、Math
、BigInt
等:
JSON[Symbol.toStringTag]; // 輸出:"JSON"
Math[Symbol.toStringTag]; // 輸出:"Math"
BigInt.prototype[Symbol.toStringTag]; // 輸出:"BigInt"
6. 小結
typeof 經常被用來檢測基礎類型,但是不夠準確,無法區分數組、對象、null,更精準的檢測應考慮使用 Object.prototype.toString
方法。