1. 程式人生 > 其它 >前端開發系列037-基礎篇之型別檢測

前端開發系列037-基礎篇之型別檢測

title: '前端開發系列037-基礎篇之型別檢測'
tags:
  - javaScript系列
categories: []
date: 2017-09-18 20:22:13
本文介紹 JavaScript 中的資料型別,以及這些資料的型別檢測方式。

JavaScript 存在很多中資料型別,按照一貫的區分方式,我們把這些資料型別分為兩大類,分別是原始(基本)資料型別和物件型別。

其中基本( 原始 )資料型別有6種,分別是 null undefined boolean string number symbol,物件型別則為object,物件型別中包含Object、Array、RegExp、Function、Error、Set、Map、WeakMap

等。

在JavaScript 程式碼中我們經常需要對資料的型別進行檢查,檢查資料型別的方式主要有四種:

- typeof     關鍵字
- instanceof 關鍵字
- Object.prototype.toString.call() 
- constructor 構造器屬性
typeof 關鍵字

typeof 關鍵字是檢查資料型別最簡單也最常用的方法。

console.log(typeof "abc")                   /* string */
console.log(typeof 12345)                   /* number */
console.log(typeof true)                    /* boolean */
console.log(typeof undefined)               /* undefined */
console.log(typeof Symbol())                /* symbol */
console.log(typeof null)                    /* object */
console.log(typeof typeof typeof Symbol())  /* string */

console.log(typeof {})                      /* object */
console.log(typeof [])                      /* object */
console.log(typeof /abc/)                   /* object */
console.log(typeof function(){})            /* function */

我們在使用typeof 關鍵字的時候,有一些注意點。

首先就是 typeof null 得到的結果為object 而非null,這在 JavaScript 語言中被認為(承認)是一個設計錯誤,typeof null 被判定為 object 的原因在於 null 本身表示什麼都沒有,所以在計算機中表示為一串0,而計算機內部處理的時候如果讀取的資料總是以000開頭,那麼就將被認為是物件型別的資料。

其次,typeof 關鍵字在物件型別進行運算的時候得到的都是object,只有函式型別比較特殊得到的是function,也就是說我們無法通過該關鍵字來檢查給定的資料是否是陣列、或者是正則表示式等等。如果需要獲取這些結構的資料型別,或者說是獲取這些資料的建構函式,常用的方式是借用 Object 原型物件上面的 toString 方法來實現,該方法總是會返回一個[object 建構函式]

結構的字串。

Object.prototype.toString 方法

JavaScript 語言中的Object.prototype.toString方法總是會返回[object 建構函式]結構的字串,其中方括號中前面的object表明該資料是物件型別的,後面的則是建立該例項的建構函式。

如果是一個普通的物件,譬如{name:"zs"},在呼叫該方法的時候 [程式碼為:({name:"zs"}).toString()]得到的結果為[object object], 但因為原型鏈方法覆蓋的問題,陣列或其它型別的資料則無法直接呼叫該方法。對於除普通物件外的其它物件型別的資料而言,它們需要通過 call 或 apply 繫結 this 的方式才能呼叫該方法。

console.log(({}).toString())                              /* [object Object] */
console.log(Object.prototype.toString.call({}))           /* [object Object] */
console.log(Object.prototype.toString.call([]))           /* [object Array] */
console.log(Object.prototype.toString.call(new Function)) /* [object Function]*/
console.log(Object.prototype.toString.call(new RegExp))   /* [object RegExp] */
console.log(Object.prototype.toString.call(new Error))    /* [object Error] */
console.log(Object.prototype.toString.call(new Set))      /* [object Set] */
console.log(Object.prototype.toString.call(new Map))      /* [object Map] */

一般而言Object.prototype.toString 方法很好用,But 它也不是萬能的,譬如它無法處理自定義型別的型別檢查( 包括自定義建構函式 和 Class 等)。

function Person() {};
let p = new Person;

class Animal {};
let a = new Animal;
console.log(Object.prototype.toString.call(p)) /* [object Object] */
console.log(Object.prototype.toString.call(a)) /* [object Object] */

對於自定義型別的資料,我們需要通過 instanceof 或者是 constructor 來進行檢查。

instanceof 關鍵字 && constructor 構造器屬性

JavaScript 語言中的 instanceof 關鍵字用於檢查某個物件是否是指定建構函式建立的例項物件,在檢查的時候會檢查整條原型鏈。如果準確點說,那麼instanceof 實際上檢查的是某個物件( 左值 )是否在指定建構函式( 右值 )的原型鏈上面,如果在那麼就返回 true ,否則的話就返回 false。

console.log(p instanceof Person)                            /* true */
console.log(a instanceof Person)                            /* false */
console.log(a instanceof Animal)                            /* true */
console.log(a instanceof Function, a instanceof Object)     /* false true */

console.log(a.constructor == Person);                       /* false */
console.log(a.constructor == Animal);                       /* true */
console.log(a.constructor == Object);                       /* false */
console.log(p.constructor == Person);                       /* true */

console.log(({}).constructor == Object)                     /* true */
console.log(([]).constructor == Array)                      /* true */

這裡試著給出instanceof關鍵字的實現程式碼,其實就是個死迴圈和迴圈內原型查詢而已。

/* instanceof 實現原理 */
function mockInstanceof(instance,constructor){

  let B = constructor.prototype;
  let A = instance.__proto__;

  while(true)
  {
    /* Object.prototype.__proto__ -> null */
    if (A == null){
      return false;
    }
    if (A === B){
      return true;
    }
    A = A.__proto__;   
  }

}

/* 測試資料 */
function Person() { };
function Boy() { };
let p = new Person;
let b = new Boy;

console.log(mockInstanceof(p, Person))          /* true */
console.log(mockInstanceof(p, Object))          /* true */
console.log(mockInstanceof(b, Boy))             /* true */
console.log(mockInstanceof(b, Person))          /* false */