tcking/jsriver

不靠谱的typeof

Opened this issue · 0 comments

typeof是唯一一个可以与未声明变量运算的操作符,因此最常用用法就是,判断变量是否为undefined,例如:

if(typefof cat !="undefined"){ 
     //如果变量没声明,用if(cat) 则会出现ReferenceError
}

typeof的本意是判断右侧变量的类型:
typeof "cat" // string
typeof function(){} // function
typeof true //boolean
typeof undefined // undefined
typeof null //object,返回object是个历史遗留问题,在ES规范没制定之前有浏览器这样实现了,后来规范就顺势而为,可以将null理解为对象的占位符,它指向一个object。

然而不靠谱的是:

typeof [] //object,期待返回array
typeof new String("cat") //object,期待返回string

typeof []返回object并不能说错了,数组本身也属于对象,但是这个结果对我们没任何意义。

其他判断类型的方式:

  1. 通过构造函数:例如 obj.constructor=="String",但是这种方式的问题是constructor属性是可以修改的
var s=new String("cat");
console.log(s.constructor);//String
s.constructor=Object
console.log(s.constructor);//Object
  1. 通过instanceof判断,例如[] instanceof Array,instanceof用的就是constructor属性,因此和第一种情况一样不靠谱,另外还有一个问题就是多框架(iframe,framesets)中,不同window中的Object也不相等,假如在子页面中:Object===top.Object // false.
  2. 通过特征类判断,看是否有特定的属性或者函数,例如数组通过是否有slice,splice等函数来判断,但是任何自定义对象都能写这两个函数,这种方式也不靠谱。

终极解决方法:
不管是new出来的还是直接量,不管有没有跨window:通过调用Object的原型对象上的toString方法,因为ES规定,Object.prototype.toString返回其内部的class属性,内部class属性正确的表示了对象的类型,那么靠谱的判断类型的方法为:

function isString(obj){
    return Object.prototype.toString.call(obj) == "[object String]";
}

function isArray(obj){
    return Object.prototype.toString.call(obj) == "[object Array]";
}

ES规定,Object.prototype.toString.call返回的内容格式是固定的,为一个字符串:"[object xxx]",其中xxx只能是String,Number,Boolean,Array,Obejct,Function
,于是个javascript框架都采用了这个终极解方法,在seajs中,为了减少其源代码大小,作者玉伯写了一个生成函数的函数isType,让isType来生成各种类型判断的函数:

function isType(type) {
  return function(obj) {
    return Object.prototype.toString.call(obj) === "[object " + type + "]"
  }
}

var isObject = isType("Object")
var isString = isType("String")
var isArray = Array.isArray || isType("Array")
var isFunction = isType("Function")

在seajs的新版本中将isType中改成了:return {}.toString.call=="[object " + type + "]",Object.prototype 改为 {},=== 改为 ==,目标都是减少代码量,其精益求精的精神让人敬佩。改为{}后会牺牲极少的性能,首先是每次调用都会新创建一个空对象,另外就是多一次查找原型对象消耗。综合考虑觉得还是Object.prototype要适合一点。

总结:
1.typeof 能安全的判断变量是否为undefined,不管是否变量是否已经定义
2.typeof在判断string和array时不靠谱,Object.prototype.toString.call(obj)是最靠谱的方法。