JavaScript 网页编程从入门到精通 (清华社"视频大讲堂"大系·网络开发视频大讲堂)
上QQ阅读APP看书,第一时间看更新

4.4 数据类型检测

JavaScript是弱类型语言,对类型没有严格限制,但是在程序中经常需要检测数据类型。对于如何检测类型,JavaScript提供了多种方法,下面将重点介绍两种。

4.4.1 使用typeof

typeof运算符专门用来测试值的类型,特别对于原始值比较有效,但是对于对象或数组类型数据,返回的值都是字符串"object",显然没有很大参考价值。

【示例1】下面代码显示使用typeof检测数据类型的方法。

      alert(typeof 1);                                  //返回字符串"number"
      alert(typeof"a");                                 //返回字符串"string"
      alert(typeof true);                               //返回字符串"boolean"
      alert(typeof{});                                  //返回字符串"object"
      alert(typeof[]);                                  //返回字符串"object"
      alert(typeof function(){});                       //返回字符串"function"
      alert(typeof undefined);                          //返回字符串"undefined"
      alert(typeof null);                               //返回字符串"object"
      alert(typeof NaN);                                //返回字符串"number"

【示例2】由于null值返回类型为object,用户可以定义一个检测简单数据类型的一般方法。

      function type(o){ //返回值类型数据的类型字符串
          return (o === null) ? "null" : (typeof o);
                          //如果是null值,则返回字符串"null",否则返回(typeof o)表达式的值
      }

上面代码避开因为null值影响基本数据的类型检测。

4.4.2 使用constructor

对于对象、数组等复杂数据,可以使用Object对象的constructor属性进行检测。constructor表示构造器,该属性值引用的是构造当前对象的函数。

【示例1】下面代码可以检测对象直接量和数组直接量的类型。

      var o = {};
      var a = [];
      alert(o.constructor==Object);                //返回true
      alert(a.constructor==Array);                 //返回true

通过上面方法,可以准确判断复杂数据是对象,还是数组。如果结合typeof运算符和constructor属性,用户基本能够完成数据类型的检测,如表4-4所示列举了不同类型数据的检测结果。测试代码如下:

表4-4 数据类型检测

      var value=1;                                //输入不同类型的值(第一列)
      alert(typeof value);                        //返回typeof运算符返回的字符串(第二列)
      alert(value.constructor);                   //返回constructor属性返回的对象(第三列)

【示例2】使用constructor属性可以检测绝大部分数据的类型,但对于undefined和null特殊值,就不能够使用constructor属性,否则会抛出异常。这时可以先把值转换为布尔值,如果为true,则说明是存在值的,然后再调用constructor属性。

      var value = undefined;
      alert(typeof value);                                    //返回字符串"undefined"
      alert(value&&value.constructor);                        //返回undefined
      var value = null;
      alert(typeof value);                                    //返回字符串"object"
      alert(value&&value.constructor);                        //返回null

另外,对于数值直接量也不能够直接使用constructor属性,下面代码将会提示语法错误:

      alert(10.constructor);

但是如果加上一个小括号,则可以检测:

      alert((10).constructor);

这是因为小括号运算符能够把数值转换为对象。

4.4.3 案例:在框架窗口检测数组类型

constructor属性是检测数据类型的最佳方法,但是在框架窗口中检测数组时容易出现问题。

先看一个示例(注意,下面示例在IE浏览器中无法正常运行):

      var iframe=document.createElement("iframe");   //创建一个浮动框架
      document.body.appendChild(iframe);             //在文档中插入该框架
      var A=window.frames[0].Array;                  //获取该浮动框架窗口包含的Array构造函数
      var a=new A();                                 //利用该浮动框架的Array构造函数创建一个数组对象
      alert(a.constructor==Array);                   //返回false
      alert(a.constructor);                          //返回Array对象的字符串提示

通过上面示例可以看到,浮动窗口的Array构造函数与当前窗口的Array构造函数并不相等,虽然它们的Array类型结构相同,但是由于所存放的位置不同(属于不同的window),所以结果也不相同。换句话说,使用constructor属性不能够很好地检测框架窗口中的数组类型。为此,需要使用其他方法进行检测。

(1)检测该数组中是否包含数组特有的方法或属性。

      function isArray(o){
          return o! =null && typeof o==="object"&&"splice" in o &&"join"in o;
      }
      alert(isArray(a));                         //返回true

该方法先判断值是否为空,如果不为空,则判断是否为object类型,然后探测该对象中是否包含数组特有的方法splice()和join()。如果找到这些方法,则说明该对象是数组类型。

(2)匹配toString()方法返回的字符串。

使用第一种方法也容易造成误解,如果用户自定义了一个包含名称为splice和join的对象,则也会把它检测为数组类型。例如:

      var o = {
          splice:1,
          join:2
      }
      alert(isArray(o));                         //返回true

但是如果把该对象转换为字符串,然后通过检测字符串中是否包含数组所特有的标志字符,也可以确定对象的类型。例如,对于数组对象来说,当直接使用toString()方法时,将转换的字符串作为数组元素的值。如果没有元素,则返回空字符串。

      alert([].toString());                        //返回""

然而使用call()或者apply()方法调用toString()方法时,返回的字符串就是"[object Array]"。所以可以这样设计:

      function isArray(o) {
          return Object.prototype.toString.call(o) ==="[object Array]";
      }
      alert(isArray(a));                         //返回true

在调用toString()方法时,必须指定该方法的作用域路径(原型方法的初始位置),否则系统因为无法找到toString()方法而报错。这样返回的字符串就可以包含“Array”标志字符,然后通过字符串比较,就可以解决跨窗口判定对象是否为数组类型。

4.4.4 案例:设计完善的数据类型检测工具

使用toString()方法可以设计一种更安全的检测JavaScript数据类型的方法,用户还可以根据开发需要进一步补充检测类型的范围。

设计思路:

首先,仔细分析不同类型对象的toString()方法返回值,会发现由Object对象定义的toString()方法返回的字符串形式总是:

      [object class]

其中object表示对象的通用类型,class表示对象的内部类型,内部类型的名称与该对象的构造函数名对应。例如,Array对象的class为“Array”, Function对象的class为“Function”, Date对象的class为“Date”,内部Math对象的class为“Math”,所有Error对象(包括各种Error子类的实例)的class为“Error”。

客户端JavaScript的对象和由JavaScript实现定义的其他所有对象都具有预定义的特定class值,如“Window”、“Document”和“Form”等。用户自定义对象的class为“Object”。

class值提供的信息与对象的constructor属性值相似,但是class值是以字符串的形式提供这些信息的,这在特定的环境中是非常有用的。如果使用typeof运算符来检测,则所有对象的class值都为“Object”或“Function”,所以不能够提供有效信息。

但是,要获取对象的class值的唯一方法是必须调用Object的原型方法toString(),因为很多类型对象都会重置Object的toString()方法,所以不能直接调用对象的toString()方法。

例如,下面对象的toString()方法返回的就是当前UTC时间字符串,而不是字符串“[object Date]”。

      var d = new Date();
      alert(d.toString());                                //返回当前UTC时间字符串

调用Object的toString()原型方法,可以通过调用Object.prototype.toString对象的默认toString()函数,再调用该函数的apply()方法在想要检测的对象上执行即可。例如,结合上面的对象d,具体实现代码如下:

      var d = new Date();
      var m = Object.prototype.toString;
      alert(m.apply(d));                                 //返回字符串"[object Date]"

明白了上面的技术细节,下面就是一个比较完整的数据类型安全检测方法源代码:

      //安全检测JavaScript基本数据类型和内置对象
      //参数:o表示检测的值
      //返回值:返回字符串"undefined"、"number"、"boolean"、"string"、"function"、"regexp"、"array"、"date"、"error"、"object"或"null"
      function typeOf(o){
          var _toString = Object.prototype.toString;
          //获取对象的toString()方法引用
          //列举基本数据类型和内置对象类型,你还可以进一步补充该数组的检测数据类型范围
          var _type ={
            "undefined" : "undefined",
            "number" : "number",
            "boolean" : "boolean",
            "string" : "string",
            "[object Function]" : "function",
            "[object RegExp]" : "regexp",
            "[object Array]" : "array",
            "[object Date]" : "date",
              "[object Error]" : "error"
          }
          return _type[typeof o] || _type[_toString.call(o)] || (o ? "object" :
                    "null");
          //通过把值转换为字符串,然后匹配返回字符串中是否包含特定字符进行检测
        }

应用示例:

      var a = Math.abs;
      alert(typeOf(a));                                      //返回字符串"function"

上述方法适用于JavaScript基本数据类型和内置对象,但是对于自定义对象是无效的。因为自定义对象被转换为字符串后,返回的值是没有规律的,且不同浏览器返回值也是不同的。因此,如果要检测非内置对象,只能够使用constructor属性和instanceof运算符来实现。