JS專題之資料型別和型別檢測

南波發表於2018-12-15

本文共 1750 字,讀完只需 7 分鐘

資料型別有哪些?

ECMAScript 的資料型別分為簡單資料型別(也被稱為基本資料型別,原始資料型別):

  1. Undefined
  2. Null
  3. String
  4. Number
  5. 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
複製程式碼

JS專題之資料型別和型別檢測

引用資料型別

除了基本的資料型別外,剩下的就是引用型別,統稱為 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
複製程式碼

JS專題之資料型別和型別檢測

一、為什麼要進行型別檢測?

JavaScript 是動態語言,動態語言和靜態語言的區別在於:型別是在編譯時檢查(靜態)還是在執行時檢查(動態)。

基於動態型別的特點,如果不注意進行型別檢測,JS 很容易在執行程式碼發生才發現報錯。

舉個列子:
函式在定義時,引數可以是任何型別,但是函式在實際執行的時候,傳入的實參可能是其他同事函式的返回值,也可能是後臺的返回值,假如不符合程式碼邏輯裡要求的資料型別,那麼就會報錯導致 bug 影響程式執行。

因此,我們不光要做型別檢測,也應該給自己的函式註釋好引數型別和返回值型別,還要和後端定義好介面資料型別格式。

當比較的兩個值的型別不同的時候 == 運算子會進行型別轉換,但是當兩個值的型別相同的時候,即使是 == 也相當於是 ===;=== 在比較兩個值的時候,還會比較值的資料型別。

二、typeof 方式

typeof 的返回值總是字串,字串的可能值有:

  1. undefined
  2. boolean
  3. number
  4. string
  5. symbol
  6. object
  7. 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 () {
    
});
複製程式碼

還有一個很常見的應用場景當然是後臺返回資料的型別的檢測了。

總結

js 是動態語言,資料型別的檢查是在執行時執行,為了避免程式碼莫名其妙報錯,所以做好資料型別的檢測很有必要。

歡迎關注我的個人公眾號“謝南波”,專注分享原創文章。

JS專題之資料型別和型別檢測

掘金專欄 JavaScript 系列文章

  1. JavaScript之變數及作用域
  2. JavaScript之宣告提升
  3. JavaScript之執行上下文
  4. JavaScript之變數物件
  5. JavaScript之原型與原型鏈
  6. JavaScript之作用域鏈
  7. JavaScript之閉包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值傳遞
  11. JavaScript之例題中徹底理解this
  12. JavaScript專題之模擬實現call和apply
  13. JavaScript專題之模擬實現bind
  14. JavaScript專題之模擬實現new
  15. JS專題之事件模型
  16. JS專題之事件迴圈
  17. JS專題之去抖函式
  18. JS專題之節流函式

相關文章