本文深入解析JavaScript中类型判断的演进过程。首先指出typeof在null处理上的局限,随后介绍基于Object.prototype.toString的标准实现。接着通过闭包缓存和jQuery源码思路优化性能,并补充了window对象与DOM元素的判断方法,提供高效且准确的类型检测方案。
JavaScript类型判断基础
在JavaScript的类型转换机制中,typeof运算符存在一个已知缺陷:它会将null识别为object类型。此外,typeof返回的类型信息较为有限。为了获得更准确的结果,通常采用Object.prototype.toString方法来实现类型判断。
类型检测的演进
第一版
function isArray(value){
return Object.prototype.toString.call(value) === "[object Array]";
}
function isFunction(value){
return Object.prototype.toString.call(value) === "[object Function]";
}
然而,如果采用逐一判断数组、函数、对象等类型的方式,代码显得过于繁琐且过程化,缺乏简洁性。
第二版
为了提升使用体验,我们设计type(obj)函数以返回对应的类型字符串。考虑到typeof返回的是小写形式,我们的实现也保持一致,返回小写的标准类型名。
function type(obj){
// -1 代表截止到倒数一位
return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase()
}
type([]) // "array"
虽然这种方式可行,但每次调用时都需要对判断结果进行slice截取和toLowerCase转换,这在频繁调用时可能带来性能损耗。由于需要判断的类型种类有限,我们可以利用对象提前缓存可能的结果,从而优化性能。
第三版
//将types放外面 而不是放在type函数里面, 利用闭包,优化性能,不用每次判断都声明一次typess
var types = {
'[object Function]': 'function',
'[object Number]': 'number',
...
}
function type(obj) {
var str = Object.prototype.toString.call(obj)
return types[str]
}
当然,上述types对象的定义方式还可以进一步优化,以借鉴更成熟的源码实现。
// 参考自jquery源码
var types = {}
当然也可以直接用数组存储
"Boolean Number String Function Array Date RegExp Object Error".split(" ").forEach(function(e,i){
types [ "[object " + e + "]" ] = e.toLowerCase();
}) ;
特殊对象与DOM元素判断
判断window对象
利用window对象的window属性等于自身的特性进行判断。
function isWindow( obj ) {
// obj !== undefined 是为了防止没传参数的时候后面报错
// Uncaught TypeError: Cannot read property 'window' of undefined的错误
return obj !== undefined && obj === obj.window;
}
判断是不是dom元素
isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
}