JavaScript系列之類型判斷

類型判斷在 web 開發中較爲常見,簡單的有判斷數字仍是字符串,判斷是數組仍是對象稍微複雜一些,再複雜一點的有判斷日期、正則、錯誤類型,再再複雜一點還有好比判斷 plainObject、空對象、Window 對象等等。javascript

經過本文,我將嘗試概述Javascript中類型判斷的幾種方法(基於ES5的規範)。java

typeof

typeof判斷類型方法算是咱們最最經常使用的了,直接上代碼:git

typeof 3 // "number"
typeof "abc" // "string"
typeof {} // "object"
typeof true // "boolean"
typeof undefined // "undefined"
typeof function(){} // "function"
複製代碼

以上這些類型都比較正常,直到:github

typeof null // "object"
複製代碼

這顯然是一個錯誤。 這可能會在之後的ECMAScript哪一個版本中修復,返回值將爲「null」。web

除此以外 Object 下還有不少細分的類型,好比 ArrayDateRegExpError 等。若是用 typeof 去檢測這些類型,舉其中幾個例子:數組

var array1 = []
var array2 = new Array();
var date = new Date();
var error = new Error();
console.log(typeof array1); // object
console.log(typeof array2); // object
console.log(typeof date); // object
console.log(typeof error); // object
複製代碼

所以,typeof很是善於區分不一樣類型的原始值,並區分它們和對象,但在區分不一樣類型的對象(包括數組和null)時徹底沒用,那這該怎麼區分?有沒有更好的解決方法呢?函數

Object.prototype.toString

JS做爲一門越發成熟的語言,解決方法固然有!嘿嘿~這就是Object.prototype.toStringui

Object.prototype.toString看起來是一長串字母,看起來比較複雜誒~爲了講清楚,在toString方法被調用時,是會執行下面的操做步驟的:this

  1. 若是this的值爲undefined,則返回"[object Undefined]".
  2. 若是this的值爲null,則返回"[object Null]".
  3. 讓O成爲調用ToObject(this)的結果.
  4. 讓class成爲O的內部屬性[[Class]]的值.
  5. 最後返回由 "[object "class"]" 三個部分組成的字符串.

經過規範,咱們至少了解了調用 Object.prototype.toString 最終會返回一個由 "[object "class"]" 組成的字符串,而 class 是要判斷的對象的內部屬性。spa

看這些規範仍是隻知其一;不知其二的狀態吧,直接上代碼直觀一點:

console.log(Object.prototype.toString.call(3)) // [object Number]
console.log(Object.prototype.toString.call([1, 2, 3])) // [object Array]
console.log(Object.prototype.toString.call({})) // [object Object]
console.log(Object.prototype.toString.call(null)) // [object Null]

var date = new Date();
console.log(Object.prototype.toString.call(date)) // [object Date]
複製代碼

咱們能夠看到這個 class 值其實就是識別對象類型的關鍵!

所以咱們能夠用 Object.prototype.toString 方法識別出更多類型!那到底能識別多少種類型呢?那仍是看代碼數個數吧~嘿嘿

var number = 1;          // [object Number]
var string = '123';      // [object String]
var bool = true;      // [object Boolean]
var unde = undefined;     // [object Undefined]
var nul = null;          // [object Null]
var obj = {}         // [object Object]
var array = [];   // [object Array]
var date = new Date();   // [object Date]
var error = new Error(); // [object Error]
var reg = /a/g;          // [object RegExp]
var func = function a(){}; // [object Function]

function checkTypes() {
    for (var i = 0; i < arguments.length; i++) {
        console.log(Object.prototype.toString.call(arguments[i]))
    }
}

checkTypes(number, string, bool, unde, nul, obj, array, date, error, reg, func)

//打印出
[object Number]
[object String]
[object Boolean]
[object Undefined]
[object Null]
[object Object]
[object Array]
[object Date]
[object Error]
[object RegExp]
[object Function]
複製代碼

除了以上 11 種以外,還有3種:

console.log(Object.prototype.toString.call(Math)); // [object Math]
console.log(Object.prototype.toString.call(JSON)); // [object JSON]

function a() {
    console.log(Object.prototype.toString.call(arguments)); 
}
a(); // [object Arguments]
複製代碼

這裏看咱們至少能夠識別14 種類型,而[[class]] 屬性數量至少有 12 個。

寫個類庫

利用Object.prototype.toString判斷類型的方法來寫個類庫吧,此類庫來自(Axis.js)[//github.com/toddmotto/axis]:

(function (root, factory) {
  // 判斷是否使用了模塊
  if (typeof define === 'function' && define.amd) {
    // 使用AMD模塊
    define(factory);
  } else if (typeof exports === 'object') {
    // 使用CMD模塊
    module.exports = factory;
  } else {
    // 沒有使用模塊,放在全局下
    root.axis = factory();
  }
})(this, function () {
  // 嚴格模式
 'use strict';
  var exports = {};
  // 將字符串轉爲數組
  var types = 'Array Object String Date RegExp Function Boolean Number Null Undefined'.split(' ');
  // 判斷類型
  var type = function () {
    return Object.prototype.toString.call(this).slice(8, -1);
  };
  // 遍歷types,爲exports對象添加isArray、isObject...等方法
  for (var i = types.length; i--;) {
    exports['is' + types[i]] = (function (self) {
      return function (elem) {
        // type.call(elem)將type方法裏的this指針指向elem
        return type.call(elem) === self;
      };
    })(types[i]);
  }
  return exports;
});
複製代碼

使用方法也比較簡單,直接上代碼:

axis.isArray([]); // true
axis.isObject({}); // true
axis.isString(''); // true
axis.isDate(new Date()); // true
axis.isRegExp(/test/i); // true
axis.isFunction(function () {}); // true
axis.isBoolean(true); // true
axis.isNumber(1); // true
axis.isNull(null); // true
axis.isUndefined(); // true
複製代碼

考慮到實際狀況下並不會檢測 MathJSON,並且上面這種方法也檢測不了這兩種類型,因此去掉這兩個類型的檢測。同時也不能識別自定義對象類型。

constructor

typeof 也有無解的時候,那麼咱們是否還有其餘好的方法來判斷一個變量是自定義對象類型呢?

咱們知道,javascript 的全部對象都有一個 constructor 屬性,這個屬性能夠幫咱們判斷 object 數據類型,直接上代碼:

//alert(1.constructor); //報錯 數字常量無 constructor 屬性 
var num = 1;   
console.log(num.constructor == Number); //true 
console.log("miqilin".constructor == String); //true 
var str = "miqilin";   
console.log(str.constructor == String); //true 
var obj= null;   
console.log(obj.constructor); //報錯,null 沒有 constructor 屬性 
var none = undefined;   
console.log(obj.constructor); //報錯,undefined 沒有 constructor 屬性 
複製代碼

能夠看出,數字型常量,nullundefined 都沒有 constructor 屬性。

以前覺得到這就所有分析完了,看了多篇外文才知道原來還有可挖掘的東西,來看下面的代碼:

function Animal() {   
}   
function Cat() {   
}   
Cat.prototype = new Animal();   
Cat.prototype.CatchMouse = function () {   
//do some thing 
}   
var obj = new Cat();   
console.log(obj.constructor == Cat); //false ??由於 Cat.prototype不在obj的原型鏈上 
console.log(obj.constructor == Animal); //true 理解 
複製代碼

原來對於原型鏈繼承的狀況,constuctor 也不怎麼好用了。那怎麼辦呢?

instanceof

嘿嘿~原來還有一種方法能夠解決這種困境,那就是 instanceofinstanceof 運算符會告訴您對象是不是某種類型的實例, 這裏所謂的「類型」其實就是構造函數。直接上代碼:

function Animal() {   
}   
function Cat() {   
}   
Cat.prototype = new Animal();   
Cat.prototype.CatchMouse = function () {   
//do some thing 
}   
var obj = new Cat();   
console.log(obj instanceof Cat); //true 毫無疑問 
console.log(obj instanceof Animal); //true 能夠理解
複製代碼

instanceof 適用於全部原生類型:

[1, 2, 3] instanceof Array // true
/abc/ instanceof RegExp // true
({}) instanceof Object // true
(function(){}) instanceof Function // true
複製代碼

可是 instanceof 不適用於原始類型:字符串,數字,布爾值:

3 instanceof Number // false
true instanceof Boolean // false
'abc' instanceof String // false
複製代碼

因此這裏constructor又有點優點了,能夠適用於原始類型numberstringboolean的判斷(constructor小節有例子)。

小結

雖然檢查任何一種特定類型真的不是那麼難,但你可能不得不在此過程當中作出不少選擇,勢必會引發一些混亂。所以,瞭解全部不一樣的選項會有所幫助,如下是對四種方法可識別類型的簡單歸納:

typeof:

  • 能夠是標準類型(Null 除外)
  • 不可識別具體的對象類型(Function 除外)

Object.prototype.toString:

  • 但是識別標準類型及內置對象類型(例如,Object, Date, Array
  • 不能識別自定義對象類型

constructor:

  • 能夠識別標準類型(Undefined/Null 除外)
  • 可識別內置對象類型
  • 可識別自定義對象類型

instanceof:

  • 不可判別原始類型
  • 可判別內置對象類型
  • 可判別自定義對象類型

類型轉換的圖形化表示(其中紅色單元格表示該判斷方式不支持的類型):

還有更復雜的判斷好比 plainObject、空對象、Window對象、類數組對象等,還未涉及,後續也會增長。 敬請關注!

本人Github連接以下,歡迎各位Star

github.com/miqilin21/m…

相關文章
相關標籤/搜索