JS中一共有7种数据类型
- 5种简单数据类型: boolean、number、string、undefined、null
- 1种复杂数据类型: object
- ES6新引入的类型: Symbol
那么怎么去检测一个变量是什么数据类型呢?一共有四种方式
一、 typeof
第一个想到的肯定是typeof
1.1 typeof检测object、null、数组:Object
但是typeof用于检测基本数据类型很有效。 但引用类型就不行了,因为typeof检测object、null、数组都会返回object。
1.2 typeof 检测正则表达式:function || Object
- safari5之前和谷歌7之前版本中,会返回"function"。
- 而在IE 和火狐中,正则表达式会返回"object"
ECMA-262 规定任何在内部实现了[[call]]方法的对象都应该在typeof检测时返回function
所以要想知道他是哪种引用类型就使用instanceof
二、instanceof
- instanceof检测基本数据类型,始终返回false
- instanceof检测和object关系时,始终都返回true
- instanceof 检测实例和原型链中出现的构造函数之间的关系,返回true
因为Array是Object的子类
每创建一个函数,就会同时创建他的prototype对象,这个对象会自动获得constructor属性。
2.1: 三种基本包装类型
为了便于基本类型值,ES还提供了3个特殊的引用类型:Boolean、Number、String。这些类型和其他的引用类型相似,并且具备各自基本类型对应的特殊行为。也就是使基本类型有了方法,为了实现这种直观的操作,
后台自动完成了一系列的操作:
- 创建一个String类型的实例
- 是实例上调用指定的方法
- 销毁实例
变成代码就是下面三步:
var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;
至此,基本的字符串值就变得跟对象一样了。而且同样适用于:Boolean、Number。
- Boolean类型 重写了valueof
- Number类型 重写了valueof、toLocaleString()、toString()方法
2.2:引用类型和基本包装类型的主要区别:对象的生存期
- 使用new 操作符创建的引用类型的实例,在执行流离开当前作用域之前一直保存在内存中。
- 而自动创建的基本包装类型的对象,
只存在于一行代码的执行瞬间,然后就立即被销毁,所以s1.color = 'red',打印的时候是undefined
2.3: Object 构造函数也会像工厂方法一样
根据传入的值类型返回相应的基本包装类型的实例
var obj = new Object('sone shd') // 创建String的实例
obj instanceof String; //true
2.4: 弊端
但是对于检测数组,对于一个网页或者一个全局作用域来说,使用instanceof没有问题,但是如果网页中包含多个框架,那实际上有2个全局作用域,从而存在两个不同的数组构造函数,如果从一个框架向另一个框架传入数组,那么传入的数组和第二个框架中原生创建的数组分别具有不同的构造函数。所以ES5新增了
三、Array.isArray
支持的浏览器有IE9+ Firefox 4+ Safari 5+ Opera 10.5+ Chrome,要在没有实现这个方法的浏览器中检测数组则使用下面的方法
四、Object.prototype.toString.call(value)
背景
- 因为typeof无法准确检测引用类型,比如null
- 而instanceof在多个全局作用域下对数组检测的也不准确,在检测某个对象到底是原生的还是开发人员自定义的时候,也有问题
- 因为浏览器原生开始支持JSON了, 开发人员很难知道JSON 是原生的还是Douglas Crockford JSON库。
那么解决以上问题就出现了最终答案:
任何值调用Object的.toString(),都会返回[Object NativeConstructorName],
但不能检测非原生构造函数的函数名,因此开发人员开的构造函数都将返回[Object Object]
原生数组的构造函数名和全局作用域无关,所以可以准确的检测数组
总结:
- 确定一个值是哪种基本类型可以使用typeof 操作符
- 而确定一个值是哪种引用类型可以使用instanceof操作符
- Object.prototype.toString.call(value)可作为最终的安全检测