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…

相关文章
相关标签/搜索