JS專題之資料型別和型別檢測
ECMAScript 的資料型別分為簡單資料型別(也被稱為基本資料型別,原始資料型別):
- Undefined
- Null
- String
- Number
- Boolean
在 ES6 中新增一個簡單資料型別 Symbol,所以簡單資料型別總共有 6 個,還有複雜資料型別(也叫作引用資料型別):Object。
所有 js 中所有的值最終都將是以上 7 種資料型別之一。
基本資料型別
基本資料型別是儲存在 棧
的資料結構中的,是按值訪問的,基本資料型別的值不會變 (值本身無法被改變) 資料之間進行的是它們 值
的比較。
看程式碼:
var a,b; a = "jay_chou"; b = a; console.log(a);// jay_chou console.log(b);// jay_chou a = "coldplay";// 改變 a 的值,並不影響 b 的值 console.log(a);// coldplay console.log(b);// jay_chou 複製程式碼

引用資料型別
除了基本的資料型別外,剩下的就是引用型別,統稱為 Object 型別, 細分的話,有 Object 型別,Array 型別,Date 型別, Regexp 型別,function, Math 型別;
引用資料型別的特點:
- 引用資料型別的值儲存的是物件在記憶體中的地址
- 引用資料型別的值是可變的
- 引用資料型別的比較是引用的比較
- 另外:js 不能直接操作物件的記憶體空間 比方:
var a = { name: "jay_chou" } var b = a; var c = { name: "jay_chou" } a === b; // true a === c; // false 複製程式碼

一、為什麼要進行型別檢測?
JavaScript/">JavaScript 是動態語言,動態語言和靜態語言的區別在於:型別是在編譯時檢查(靜態)還是在執行時檢查(動態)。
基於動態型別的特點,如果不注意進行型別檢測,JS 很容易在執行程式碼發生才發現報錯。
舉個列子:
函式在定義時,引數可以是任何型別,但是函式在實際執行的時候,傳入的實參可能是其他同事函式的返回值,也可能是後臺的返回值,假如不符合程式碼邏輯裡要求的資料型別,那麼就會報錯導致 bug 影響程式執行。
因此,我們不光要做型別檢測,也應該給自己的函式註釋好引數型別和返回值型別,還要和後端定義好介面資料型別格式。
當比較的兩個值的型別不同的時候 ==
運算子會進行型別轉換,但是當兩個值的型別相同的時候,即使是 ==
也相當於是 ===
; ===
在比較兩個值的時候,還會比較值的資料型別。
二、typeof 方式
typeof 的返回值總是字串,字串的可能值有:
- undefined
- boolean
- number
- string
- symbol
- object
- function
typeof 其實是一元操作符,和 + - * /
一樣,不是一個函式,進行比較的時候,typeof 後面可以跟 ()
, 也可以不跟。
undefined:
typeof undefined; // undefined
很多庫因為考慮到 undefined 可能會被意外重寫,用 void 0
來判斷是否是 undefined。
var isUndefined = function (obj) { return obj === void 0; } 複製程式碼
MDN 上對 void 詞條的說明是:
The void operator evaluates the given expression and then returns undefined.
意思是說 void 運算子能對給定的表示式進行求值,然後返回 undefined。也就是說,void 後面你隨便跟上一個表示式,返回的都是 undefined,都能完美代替 undefined。
string, number, boolean, symbol, function, object :
typeof "abc"; // string
typeof 123; // number
typeof NaN; // number
typeof true; // boolean
typeof Symbol(); // symbol
typeof function () {}; // function
typeof {}; // object
null
typeof null; // object!!!
js 中,不同的物件在底層都表示為二進位制,在Javascript中二進位制前三位都為 0 的話會被檢測為 Object 型別,null 的二進位制表示全為0,自然前三位也是 0,所以執行 typeof 時會返回 "object"。
**Array, Date, Regexp, Math: **
typeof []; // object
陣列的判斷不考慮相容性的話,可以用 Array.isArray() 方法進行檢測。
typeof new Date(); // object
typeof /s/g; // object
typeof Math; // object
typeof new String("foo"); // object!!!
typeof new Number(123); // object!!!
typeof new Boolean(true); // object!!!
typeof new Function(""); // function
typeof new Error(); // object
基於以上,基本型別大部分都能被準確檢測並返回正確的字串,並不是所有的型別都能被正確檢測出來。所以在實際應用中,避免用基本包裝型別 new Number()
這種方式來初始化資料。
三、instanceof 方式
上面說到基本包裝型別:new Number(), new Boolean, New String();
它們用 typeof 判斷,會檢測成物件。那針對基本包裝型別可以用 instanceof 來判斷。
instanceof 運算子可以用來檢測某個建構函式的 prototype 屬性是否存在於另外一個要檢測物件的原型鏈上。
// 定義建構函式 function Person(){} var person1 = new Person(); // 因為 Object.getPrototypeOf(person1) === Person.prototype person1 instanceof Person;// true 複製程式碼
現在我們檢測一下:
var str = new String('abc');// 基本包裝型別 var strValue = "foo"; strValue instanceof String;// false str instanceof String;// true str instanceof Object;// true [] instanceof Array;// true [] instanceof Object;// true 複製程式碼
如果我們修改建構函式的原型後,這個方法也不怎麼靠譜了:
var str = new String('abc'); str.__proto__ = Object.prototype; str instanceof String;// false !!! str instanceof Object;// true 複製程式碼
四、toString() 方式
ECMAScript 的 Boolean 值、數字和字串的原始值的有趣之處在於它們是偽物件,這意味著它們實際上具有屬性和方法。
ECMAScript 定義所有物件都有 toString() 方法,無論它是偽物件,還是真物件。因為 String 型別屬於偽物件,所以它一定有 toString() 方法。
使用 Object.prototype.toString
方法, 可以獲取到變數的準確的資料型別.
Object.prototype.toString.call(1);// '[object Number]' Object.prototype.toString.call('1');// '[object String]' Object.prototype.toString.call(NaN);// '[object Number]' Object.prototype.toString.call(foo);// '[object Function]' Object.prototype.toString.call(Symbol());// "[object Symbol]" Object.prototype.toString.call([1,2,3]);// '[object Array]' Object.prototype.toString.call(undefined);// '[object Undefined]' Object.prototype.toString.call(null);// '[object Null]' Object.prototype.toString.call(true);// '[object Boolean]' Object.prototype.toString.call(/^s/g);// '[object RegExp]' Object.prototype.toString.call(Math);// "[object Math]" Object.prototype.toString.call(new Error());// "[object Error]" Object.prototype.toString.call(new Date());// "[object Date]" 複製程式碼
toString 就能解決基本包裝型別的檢測錯誤和 instanceof
的檢測不安全。
基於 toString 我們可以構造很多工具函式用來檢測資料型別,這一塊實現的方案很多,本文就按下不表。
五、應用場景
js 型別檢測常見的應用場景: 應用場景:新增預設值
function foo(a, b) { // 方式一 if (typeof b=== 'undefined') { b = 0; } // 方式二:不適用foo(10, false)這種情況 b = b || 0; } foo(10); 複製程式碼
回撥函式呼叫
function fn(callback) { //typeof callback === 'function' ? callback() : null; callback && callback(); } fn(function () { }); 複製程式碼
還有一個很常見的應用場景當然是後臺返回資料的型別的檢測了。