JavaScript是弱类型语言,对类型没有严格限制,但是在程序中经常需要检测数据类型。对于如何检测类型,JavaScript提供了多种方法,下面将重点介绍两种。
typeof运算符专门用来测试值的类型,特别对于原始值比较有效,但是对于对象或数组类型数据,返回的值都是字符串"object",显然没有很大参考价值。
【示例1】 下面代码显示使用typeof检测数据类型的方法。
【示例2】 由于null值返回类型为object,用户可以定义一个检测简单数据类型的一般方法。
上面代码避开因为null值影响基本数据的类型检测。
对于对象、数组等复杂数据,可以使用Object对象的constructor属性进行检测。constructor表示构造器,该属性值引用的是构造当前对象的函数。
【示例1】 下面代码可以检测对象直接量和数组直接量的类型。
通过上面方法,可以准确判断复杂数据是对象,还是数组。如果结合typeof运算符和constructor属性,用户基本能够完成数据类型的检测,如表4-4所示列举了不同类型数据的检测结果。测试代码如下:
表4-4 数据类型检测
【示例2】 使用constructor属性可以检测绝大部分数据的类型,但对于undefined和null特殊值,就不能够使用constructor属性,否则会抛出异常。这时可以先把值转换为布尔值,如果为true,则说明是存在值的,然后再调用constructor属性。
另外,对于数值直接量也不能够直接使用constructor属性,下面代码将会提示语法错误:
alert(10.constructor);
但是如果加上一个小括号,则可以检测:
alert((10).constructor);
这是因为小括号运算符能够把数值转换为对象。
constructor属性是检测数据类型的最佳方法,但是在框架窗口中检测数组时容易出现问题。
先看一个示例(注意,下面示例在IE浏览器中无法正常运行):
通过上面示例可以看到,浮动窗口的Array构造函数与当前窗口的Array构造函数并不相等,虽然它们的Array类型结构相同,但是由于所存放的位置不同(属于不同的window),所以结果也不相同。换句话说,使用constructor属性不能够很好地检测框架窗口中的数组类型。为此,需要使用其他方法进行检测。
(1)检测该数组中是否包含数组特有的方法或属性。
该方法先判断值是否为空,如果不为空,则判断是否为object类型,然后探测该对象中是否包含数组特有的方法splice()和join()。如果找到这些方法,则说明该对象是数组类型。
(2)匹配toString()方法返回的字符串。
使用第一种方法也容易造成误解,如果用户自定义了一个包含名称为splice和join的对象,则也会把它检测为数组类型。例如:
但是如果把该对象转换为字符串,然后通过检测字符串中是否包含数组所特有的标志字符,也可以确定对象的类型。例如,对于数组对象来说,当直接使用toString()方法时,将转换的字符串作为数组元素的值。如果没有元素,则返回空字符串。
alert([].toString()); //返回""
然而使用call()或者apply()方法调用toString()方法时,返回的字符串就是"[object Array]"。所以可以这样设计:
在调用toString()方法时,必须指定该方法的作用域路径(原型方法的初始位置),否则系统因为无法找到toString()方法而报错。这样返回的字符串就可以包含“Array”标志字符,然后通过字符串比较,就可以解决跨窗口判定对象是否为数组类型。
使用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]”。
调用Object的toString()原型方法,可以通过调用Object.prototype.toString对象的默认toString()函数,再调用该函数的apply()方法在想要检测的对象上执行即可。例如,结合上面的对象d,具体实现代码如下:
明白了上面的技术细节,下面就是一个比较完整的数据类型安全检测方法源代码:
应用示例:
上述方法适用于JavaScript基本数据类型和内置对象,但是对于自定义对象是无效的。因为自定义对象被转换为字符串后,返回的值是没有规律的,且不同浏览器返回值也是不同的。因此,如果要检测非内置对象,只能够使用constructor属性和instanceof运算符来实现。