代码改变世界

javascript类型判断

2009-07-25 01:04  BlueDream  阅读(393)  评论(0编辑  收藏  举报

在ECMAScript中值分为原始类型和引用类型.

原始类型有五种分别为:Undefined, Null, Number, Boolean, String.

引用类型包括:Object, Function, Array, Date, RegExp等.

对于数据类型的坚持有几种方法,让我们由浅入深的分析下.

1. typeof是最弱的的一种检测方法

typeof运算符只返回number, string, boolean, undefined, object, function.对于基本数据类型检测还可以满足.但对于返回对象类型的就无从下手了.如:

typeof null、typeof {}、typeof window、typeof []、typeof new Date()都会返回Object.对于null可以用value === null来进行严格检测. 但对于其他

几种就不行了.所以这里仅给出基本数据类型的检测函数:

    function type(o) {return (o === null? null : typeof(o)};
    document.write(type(
0+ "," + type(true+ "," + type(undefined) + 
    
"," + type(null+ "," + type(type));

 

2. 使用instanceof或者constructor来检测instanceof的检测原理为:运算符左侧对象的原型链是否和右侧对象的prototype属性是同一个对象。因此这就存在问题了,假如左右侧的对象不在一个页面中,这样他们的引用就不太可能是一个对象了。典型的示例就是在存在frame的页面中,如a.html通过iframe包含b.html,如果在a.html中定义var arr=[];然后再b.html中通过parent.arr instanceof Array就会返回false,因为parent.arr的原型链是和parent.Array对象的prototype属性引用的同一个对象,而和b.html中的Array的prototype属性完全没有关系。

我们看到了instanceof对于数组类型检测存在的局限性:必须在同一个页面中的对象才能正确检测。其实采用constructor属性来判断和instanceof的局限性是一样的,因为他们都是对比的对象引用是否一样。

所以JQ给了如下判断数组的方法:

    var isArray = function(arr) { return !!arr && arr.constructor == Array}
    alert(isArray([
123]));
看似完美的方法,却产生了意外,那就是当左侧对象和右侧对象不在同一个页面中,那样原型链被打断了就无法判断.在FF下运行以下代码:
    var isArray = function(arr) { return !!arr && arr.constructor == Array; }
    window.onload 
= function(){
        
var iframe = document.createElement("iframe");
        document.body.appendChild(iframe);
        
var xArray = window.frames[window.frames.length - 1].Array;
        
var arr = new xArray(123);
        alert(isArray(arr)); 
// false
    }
3. 特性检测(Duck-typing)

采用简单的假设方式来做检测,如数组对象存在concat、splice、sort等相比其它对象特有的一些特性,因此可以通过简单判断对象是否有这些特性来进行检测。如下面的一些实现例子:

//prototype1.5的实现
function isArray(object){
    
return object != null && typeof object === "object" &&
        
'splice' in object && 'join' in object;
}

//Douglas Crockford在2003年提供的方式
typeof myArray.sort == 'function';
这个方法虽然避免了2方法的问题,但由于Object的伪装性,缺点也显而易见.如:
var myArray = {'splice'1'join'2'sort'function(){}};
alert(isArray(myArray));
alert(
typeof myArray.sort == 'function')
虽然是对象但可以伪装成数组的属性,而躲过检测.
4. 采用Object.prototype.toString.call(o)来检测
为什么要用Object.prototype.toString而不是Function.prototype.toString或者其它?这是和他们的toString解释方式有关系的。下面是ECMA中对Object.prototype.toString的解释:
Object.prototype.toString( )When the toString method is called, the following steps are taken:1. Get the [[Class]] property of this object.2. Compute a string value by concatenating the three strings “[object “, Result (1), and “]”.3. Return Result (2)
其过程简单说来就是:1、获取对象的类名(对象类型)。2、然后将[object、获取的类名、]组合并返回。
ECMA中对Array有如下说明:
The [[Class]] property of the newly constructed object is set to “Array”.
因此我们用如下代码来检测数组:
    function isArray(o){
        
return Object.prototype.toString.call(o) === '[object Array]';
    }
这是个完美的解决方案,当然对于Function, Date等也都适用.
最后给个综合的方法
    var is = {
        types: [
"Array""RegExp""Date""Number""String""Object"]
    };
    
for(var i = 0,c;c = is.types[i++];){
        is[c] 
= (function(type){
            
return function(obj){
                
return Object.prototype.toString.call(obj) === '[object ' + type +']';
            }
        })(c);
    }

    alert(is[
"Array"]([1,2,3]));  //true
    alert(is["RegExp"](/[0-9]/)); // true
    alert(is["Number"]("str"));   // false
今天又追加个方法
var isArray = function(arr) { return Array.toString() == arr.constructor }