[TOC]
JS数据类型判断
JS中数据类型:
- 基本数据类型(Undefined、Null、Boolean、Number、String)
- 复杂数据类型 (Object),Object 包括:Function 、Array、RegExp、Date 等等
基本类型也称为简单类型,由于其占据空间固定,是简单的数据段,为了便于提升变量查询速度,将其存储在栈中,即按值访问。
引用类型也称为复杂类型,由于其值的大小会改变,所以不能将其存放在栈中,否则会降低变量查询速度,因此,其值存储在堆(heap)中,而存储在变量处的值,是一个指针,指向存储对象的内存处,即按址访问。
1. typeof
typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 7 种:number、boolean、symbol、string、object、undefined、function 等。
typeof '' // "string"
typeof 1 // "number"
typeof Symbol() // "symbol"
typeof true // "boolean"
typeof null // "object" 无效
typeof [] // "object" 无效
typeof function abc(){} // "function"
typeof new Date() // "object" 无效
typeof new RegExp() // "object" 无效
有些时候,typeof 操作符会返回一些令人迷惑但技术上却正确的值:
- 对于基本类型,除 null 以外,均可以返回正确的结果。
- 对于引用类型,除 function 以外,一律返回 object 类型。
- 对于 null ,返回 object 类型。
- 对于 function 返回 function 类型。
其中,null 有属于自己的数据类型 Null , 引用类型中的 数组、日期、正则 也都有属于自己的具体类型,而 typeof 对于这些类型的处理,只返回了处于其原型链最顶端的 Object 类型,没有错,但不是我们想要的结果。
2. instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 在这里需要特别注意的是:instanceof 检测的是原型。
console.log(true instanceof Boolean) // false
console.log(23 instanceof Number) // false
console.log('hello' instanceof String) // false
console.log(undefined instanceof Object) // false
console.log([1,2,3] instanceof Array) // true
console.log(null instanceof Object) // false
console.log({name: 'jack'} instanceof Object) // true
console.log(function(){} instanceof Function) // true
console.log((new Boolean()) instanceof Boolean) // true
console.log((new Number()) instanceof Number) // true
console.log((new String()) instanceof String) // true
function Person(){}
var per = new Person()
console.log(per instanceof Person) // true
function Student(){}
Student.prototype = new Person()
var haoxl = new Student()
console.log(haoxl instanceof Student);// true
console.log(haoxl instanceof Person)
从结果中看出instanceof不能区别undefined和null,而且对于基本类型如果不是用new声明的则也测试不出来,对于是使用new声明的类型,它还可以检测出多层继承关系。
instanceof 操作符的问题在于,它假定只有一个全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
var xArray = window.frames[0].Array;
var arr = new xArray(1,2,3); // [1,2,3]
console.log(arr instanceof Array); // false
针对数组的这个问题,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象本身是否为 Array 类型,而不区分该对象在哪个环境中创建。
3. constructor
当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。
当执行 var f = new F() 时,F 被当成了构造函数,f 是F的实例对象,此时 F 原型上的 constructor 传递到了 f 上,因此 f.constructor == F
可以看出,F 利用原型对象上的 constructor 引用了自身,当 F 作为构造函数来创建对象时,原型上的 constructor 就被遗传到了新创建的对象上, 从原型链角度讲,构造函数 F 就是新对象的类型。这样做的意义是,让新对象在诞生以后,就具有可追溯的数据类型。
注意:
- null 和 undefined 是无效的对象,因此没有有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
- 函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object
console.log(true.constructor === Boolean) // true
console.log((23).constructor === Number) // true
console.log('hello'.constructor === String) // true
console.log([1,2,3].constructor === Array) // true
console.log([1,2,3].constructor === Object) // false
console.log({name: 'jack'}.constructor === Object) // true
console.log((function() {}).constructor === Function) // true
function Person(){}
function Student(){}
Student.prototype = new Person()
var haoxl = new Student()
console.log(haoxl.constructor === Student) // false
console.log(haoxl.constructor === Person) // true
4. toString()
Object.prototype.toString
方法返回对象的类型字符串,因此可以用来判断一个值的类型。
返回的字符串object Object
,其中第二个Object
表示该值的构造函数。这是一个十分有用的判断数据类型的方法。
由于实例对象可能会自定义toString
方法,覆盖掉Object.prototype.toString
方法,所以为了得到类型字符串,最好直接使用Object.prototype.toString
方法。通过函数的call
方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型。
不同数据类型的Object.prototype.toString
方法返回值如下。
- 数值:返回
[object Number]
。 - 字符串:返回
[object String]
。 - 布尔值:返回
[object Boolean]
。 - undefined:返回
[object Undefined]
。 - null:返回
[object Null]
。 - 数组:返回
[object Array]
。 - arguments 对象:返回
[object Arguments]
。 - 函数:返回
[object Function]
。 - Error 对象:返回
[object Error]
。 - Date 对象:返回
[object Date]
。 - RegExp 对象:返回
[object RegExp]
。 - 其他对象:返回
[object Object]
。
Object.prototype.toString.call(2) // "[object Number]"
Object.prototype.toString.call('') // "[object String]"
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([]) // "[object Array]"
function func() {
console.log(Object.prototype.toString.call(arguments)) // [object Arguments]
}
Object.prototype.toString.call(func) // "[object Function]"
Object.prototype.toString.call(new Date()) // "[object Date]"
Object.prototype.toString.call(new Error) // "[object Error]"
Object.prototype.toString.call(/abc/) // "[object RegExp]"
Object.prototype.toString.call(Math) // "[object Math]"
Object.prototype.toString.call({}) // "[object Object]"
利用这个特性,可以写出一个比typeof
运算符更准确的类型判断函数。
var type = function (o){
var s = Object.prototype.toString.call(o);
return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};
type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"