判断JS数据类型的四种方法

Javascript可以通过typeof来判断基础数据类型,但不能够准确判断引用类型, 因此需要用到另外一个方法,那就是Object的toString
说到数据类型,我们先说一下JavaScript 中常见的几种数据类型:

基本类型:string,number,boolean

特殊类型:undefined,null

引用类型:Object,Function,Function,Array,RegExp,Date,…

很多时候我们都需要通过判断变量的数据类型来进行下一步操作,下面我们介绍常用的4种方法:

1、typeof

typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、object、undefined、function等6种数据类型。

typeof ''; // string 有效
typeof 1; // number 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Function(); // function 有效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效

typeof 可以对JS基础数据类型做出准确的判断,而对于引用类型返回的基本上都是object,
其实返回object也没有错,因为所有对象的原型链最终都指向了Object,Object是所有对象的祖宗
但当我们需要知道某个对象的具体类型时,typeof 就显得有些力不从心了

2、instanceof

nstanceof 是用来判断 A 是否为 B 的实例对,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。 在这里需要特别注意的是:instanceof检测的是原型,我们用一段伪代码来模拟其内部执行过程:

instanceof (A,B) = {
    var L = A.__proto__;
    var R = B.prototype;
    if(L === R) {
        //A的内部属性__proto__指向B的原型对象
        return true;
    }
    return false;
}

上述过程可以看出,当 A 的 proto 指向 B 的 prototype 时,就认为A就是B的实例,我们再来看几个例子:

[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true

function Person(){};
new Person() instanceof Person;

[] instanceof Object; //true
new Date() instanceof Object;//true
new Person instanceof Object;//true

我们发现,虽然 instanceof 能够判断出 [] 是Array的实例,但它认为 [] 也是Object的实例,为什么呢? 我们来分析一下[]、Array、Object 三者之间的关系: 从instanceof 能够判断出 [].proto 指向 Array.prototype, 而 Array.prototype.proto 又指向了Object.prototype,Object.prototype.proto 指向了null,标志着原型链的结束。因此,[]、Array、Object就形成了如下图所示的一条原型链:
此处输入图片的描述
从原型链可以看出,[] 的 proto 直接指向Array.prototype, 间接指向Object.prototype, 所以按照 instanceof 的判断规则,[] 就是Object的实例。当然,类似的new Date()、new Person() 也会形成这样一条原型链,因此,instanceof 只能用来判断两个对象是否属于原型链的关系, 而不能获取对象的具体类型。

3、constructor

当一个函数F被定义时,JS引擎会为F添加prototype原型,然后再在prototype上添加一个constructor属性,并让其指向F的引用。如下所示:
此处输入图片的描述
当执行 var f = new F() 时,F被当成了构造函数,f是F的实例对象,此时F原型上的constructor传递到了f上,因此f.constructor == F
此处输入图片的描述
可以看出,JS在函数F的原型上定义了constructor,当F被当作构造函数用来创建对象时,创建的新对象就被标记为了“F” 类型,使得新对象有名有姓,可以追溯。

同理,JS中的数据类型也遵守这个规则:
此处输入图片的描述
注意:

null和undefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据需要通过typeof来判断。
JS对象的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object

4、Object.prototype.toString

Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window是全局对象global的引用

需要注意的是,必须通过Object.prototype.toString.call来获取,而不能直接 new
Date().toString(), 从原型链的角度讲,所有对象的原型链最终都指向了Object,
按照JS变量查找规则,其他对象应该也可以直接访问到Object的toString方法,而事实上,大部分的对象都实现了自身的toString方法,这样就可能会导致Object的toString被终止查找,因此要用call来强制执行Object的toString方法。


本作品采用 知识共享署名 2.5 中国大陆许可协议 进行许可,欢迎转载,但转载请注明来自JayMo,并保持转载后文章内容的完整。本人保留所有版权相关权利。
本文永久链接:http://jaymo666.github.io/2017/06/20/判断JS数据类型的四种方法/