类型判断在 web 开发中较为常见,简单的有判断数字还是字符串,判断是数组还是对象稍微复杂一些,再复杂一点的有判断日期、正则、错误类型,再再复杂一点还有比如判断 plainObject、空对象、Window 对象等等。
通过本文,我将尝试概述Javascript中类型判断的几种方法(基于ES5的规范)。
typeof
typeof判断类型方法算是我们最最常用的了,直接上代码:
typeof 3 // "number"typeof "abc" // "string"typeof {} // "object"typeof true // "boolean"typeof undefined // "undefined"typeof function(){} // "function"复制代码
以上这些类型都比较正常,直到:
typeof null // "object"复制代码
这显然是一个错误。 这可能会在以后的ECMAScript哪个版本中修复,返回值将为“null
”。
除此之外 Object
下还有很多细分的类型,比如 Array
、Date
、RegExp
、Error
等。如果用 typeof
去检测这些类型,举其中几个例子:
var array1 = []var array2 = new Array();var date = new Date();var error = new Error();console.log(typeof array1); // objectconsole.log(typeof array2); // objectconsole.log(typeof date); // objectconsole.log(typeof error); // object复制代码
因此,typeof
非常善于区分不同类型的原始值,并区分它们和对象,但在区分不同类型的对象(包括数组和null
)时完全没用,那这该怎么区分?有没有更好的解决方法呢?
Object.prototype.toString
JS作为一门愈加成熟的语言,解决方法当然有!嘿嘿~这就是Object.prototype.toString
!
那 Object.protototype.toString
看起来是一长串字母,看起来比较复杂诶~为了讲清楚,在toString方法被调用时,是会执行下面的操作步骤的:
- 如果
this
的值为undefined
,则返回"[object Undefined
]". - 如果this的值为
null
,则返回"[object Null
]". - 让O成为调用
ToObject(this)
的结果. - 让class成为O的内部属性
[[Class]]
的值. - 最后返回由
"[object "
和class
和"]"
三个部分组成的字符串.
通过规范,我们至少了解了调用 Object.prototype.toString
最终会返回一个由 "[object "
和 class
和 "]"
组成的字符串,而 class
是要判断的对象的内部属性。
看这些规范还是一知半解的状态吧,直接上代码直观一点:
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)[]:
(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([]); // trueaxis.isObject({}); // trueaxis.isString(''); // trueaxis.isDate(new Date()); // trueaxis.isRegExp(/test/i); // trueaxis.isFunction(function () {}); // trueaxis.isBoolean(true); // trueaxis.isNumber(1); // trueaxis.isNull(null); // trueaxis.isUndefined(); // true复制代码
考虑到实际情况下并不会检测 Math
和 JSON
,而且上面这种方法也检测不了这两种类型,所以去掉这两个类型的检测。同时也不能识别自定义对象类型。
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 属性 复制代码
可以看出,数字型常量,null
和 undefined
都没有 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
嘿嘿~原来还有一种方法可以解决这种困境,那就是 instanceof
。instanceof
运算符会告诉您对象是否是某种类型的实例, 这里所谓的“类型”其实就是构造函数。直接上代码:
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 // falsetrue instanceof Boolean // false'abc' instanceof String // false复制代码
所以这里constructor
又有点优势了,可以适用于原始类型number
,string
和boolean
的判断(constructor
小节有例子)。
小结
虽然检查任何一种特定类型真的不是那么难,但你可能不得不在此过程中做出很多选择,势必会引起一些混乱。因此,了解所有不同的选项会有所帮助,以下是对四种方法可识别类型的简单概括:
typeof:
- 可以是标准类型(
Null
除外) - 不可识别具体的对象类型(
Function
除外)
Object.prototype.toString:
- 可是识别标准类型及内置对象类型(例如,
Object
,Date
,Array
) - 不能识别自定义对象类型
constructor:
- 可以识别标准类型(
Undefined
/Null
除外) - 可识别内置对象类型
- 可识别自定义对象类型
instanceof:
- 不可判别原始类型
- 可判别内置对象类型
- 可判别自定义对象类型
类型转换的图形化表示(其中红色单元格表示该判断方式不支持的类型):
还有更复杂的判断比如 plainObject
、空对象、Window
对象、类数组对象等,还未涉及,后续也会增加。 敬请关注!
本人Github链接如下,欢迎各位Star