小松的技术博客

六和敬

若今生迷局深陷,射影含沙。便许你来世袖手天下,一幕繁华。 你可愿转身落座,掌间朱砂,共我温酒煮茶。

JS中的类型判断(javascript框架设计学习录)

js中存在两种类型判断的方法,一种是typeof,用于判断基本类型:undefined、string、null、boolean、function、object;另一种是instance,用于检测对象类型系统。但是这两套机制只能粗略的判断类型,并不是非常精确,比如:

typeof null  //"object"
typeof new String("aa") //"object"(基本类型可以被包装)

因此更为精确的是通过Object.prototype.toString来判断,因为它直接输出对象内部的[[class]],但是由于ECMA并不规范Host对象,所以部分判断在IE低版本存在兼容性的问题,因此要具体情况具体分析:

util

var toString = Object.prototype.toString;
//利用Object.prototype.toString做类型判断
var isType = function(type){
    return function(obj){
        return toString.call(obj) == '[object '+type +']';
    }
}

isNull

var isNull = function(obj){
    return obj === null;
}

isUndefined

var isUndefined = function(obj){
    return obj === void 0;
}

可直接利用util中isType判断的系列

Number,String,Function,Date,RegExp,Error

isNaN

NaN是一个Number,但NaN != NaN:

var isNaN = function(obj){
    return isNumber(obj) && obj != obj
}

isArray

var isArray = Array.isArray || isType('Array');

isBoolean

var isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';

};

isWindow

toString返回的情况有以下几种(不同浏览器的不同版本):

  • [object Window]
  • [object global]
  • [object DOMWindow]

在低版本IE浏览器中,由于Window是BOM对象,toString的判断失效

可以根据低版本IE下window == window.document 但是 window.document != window的特性来判断:

var isWindow = function(obj){
   var class2type = {
       "[object Window]":1,
       "[object global]":1,
       "[object DOMWindow]":1
   }
   if(class2type[toString.call(obj)] === 1){
      return true;
   }
   return obj == obj.document && obj.document != document
}

isArguments

在标准浏览器中用toString是可以判断的,除此Arguments对象有一个callee方法,但是ES5标准中被移除了(虽然现在浏览器还支持,但不能保证永久支持),因此可以作为兼容方案。

var isArguments = function(obj){
    if(toString.call(obj) === "[object Arguments]"){
        return true;
    }
    return !!obj.callee;
}

isDocument

标准浏览器中使用toString输出的结果是[object HTMLDocument],旧版本采用nodeType做判断

var isDocument = function(obj){
    if(toString.call(obj) === "[object HTMLDocument]"){
        return true;
    }
    return obj.nodeType && obj.nodeType === 9
} 

isNodeList

toString会返回以下几种情况:

  • [object NodeList]
  • [object HTMLCollection]
  • [object StaticNodeList] (querySelectorAll)

NodeList有一个可读的length属性以及一个item方法,可以用它们来做兼容

var isNodeList = function(obj){
    var class2type = {
       "[object NodeList]":1,
       "[object HTMLCollection]":1,
       "[object StaticNodeList]":1
   }
   if(class2type[toString.call(obj)] === 1){
      return true;
   }
   return isFinite(obj.length) && obj.item;
}

把这些综合起来,司徒正美给出的一个非常强大的判断方法:

var class2type = {
    "[object HTMLDocument]": "Document",
    "[object HTMLCollection]": "NodeList",
    "[object StaticNodeList]": "NodeList",
    "[object DOMWindow]": "Window",
    "[object global]": "Window",
    "null": "Null",
    "NaN": "NaN",
    "undefined": "Undefined"
}

"Boolean,Number,String,Function,Array,Date,RegExp,Window,Document,Arguments,NodeList".replace(/[^, ]+/g, function(name) {
    class2type["[object " + name + "]"] = name;
});

function type(obj, str) {
    var result = class2type[(obj == null || obj !== obj) ? obj : toString.call(obj)] || obj.nodeName || "#";
    if (result.charAt(0) === "#") { //兼容旧式浏览器与处理个别情况,如window.opera
        //利用IE678 window == document为true,document == window竟然为false的神奇特性
        if (obj == obj.document && obj.document != obj) {
            result = "Window"; 
        } else if (obj.nodeType === 9) {
            result = "Document"; 
        } else if (obj.callee) {
            result = "Arguments"
        } else if (isFinite(obj.length) && obj.item) {
            result = "NodeList";
        } else {
            result = serialize.call(obj).slice(8, -1);
        }
    }
    if (str) {
        return str === result;
    }
    return result;
}
←支付宝← →微信 →
comments powered by Disqus