一、最优的数据类型判断方法
var type = function (o){
var s = Object.prototype.toString.call(o);
return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};
['Null',
'Undefined',
'Object',
'Array',
'String',
'Number',
'Boolean',
'Function',
'RegExp',
'Json',
'Date',
].forEach(function (t) {
type['is' + t] = function (o) {
return type(o) === t.toLowerCase();
};
});
type.isObject({}) // true
type.isNumber(NaN) // true
type.isRegExp(/abc/) // true
type.isRegExp(JSON) // true
type.isJson(JSON) //true
二、分析
为什么要用上面的方法,而不用typeof呢?因为typeof只能判断基础类型,RegExp、Json、Date、Null、Array都只能识别为Object类型
那为什么不直接用obj.toString()
呢?
这是因为toString为Object的原型方法,而Array、Function等类型作为Object的实例,都重写了toString方法。
不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(Function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用Object上原型toString方法(返回对象的具体类型)
我们可以验证一下,将数组的toString方法删除,看看会是什么结果:
var arr=[1,2,3];
console.log(Array.prototype.hasOwnProperty("toString"));//true
console.log(arr.toString());//1,2,3
delete Array.prototype.toString;//delete操作符可以删除实例属性
console.log(Array.prototype.hasOwnProperty("toString"));//false
console.log(arr.toString());//"[object Array]"
删除了Array的toString方法后,同样再采用arr.toString()方法调用时,不再有屏蔽Object原型方法的实例方法,因此沿着原型链,arr最后调用了Object的toString方法,返回了和Object.prototype.toString.call(arr)相同的结果。
三、补充
Object.prototype.toString.call有一个缺点,即:不能精准判断自定义对象,对于自定义对象只会返回[object Object]
而这个缺点刚好可以由instanceof
来解决
instanceof 的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
例如,使用 instanceof判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的原型,找到返回 true,否则返回 false
[] instanceof Array; // true
缺点: instanceof 只能用来判断对象类型,原始类型不可以。 并且所有对象类型 instanceof Object 都是 true,这刚好和Object.prototype.toString.call互补
function f(name) {
this.name=name;
}
var f1=new f('martin');
console.log(f1 instanceof f);//true