读书频道 > web开发 > Javascript > Effective JavaScript:编写高质量JavaScript代码的68个有效方法
第3条:当心隐式的强制转换
2013-12-07 13:55:56     我来说两句 
收藏    我要投稿   
本书共分为7章,分别涵盖JavaScript的不同主题。第1章主要讲述最基本的主题,如版本、类型转换要点、运算符注意事项和分号局限等。第2章主要讲解变量作用域,介绍此方面的一些基本概念,以及一些最佳实践经验。第  立即去当当网订购

JavaScript对类型错误出奇宽容。许多语言都认为表达式
3+true;//4

是错误的,因为布尔表达式(如true)与算术运算是不兼容的。在静态类型语言中,含有类似这样表达式的程序甚至不会被允许运行。在一些动态类型语言中,含有类似这样表达式的程序可以运行,但是会抛出一个异常。然而,JavaScript不仅允许程序运行,而且还会顺利地产生结果4!

JavaScript中有一些极少数的情况,提供错误的类型会产生一个即时错误。例如,调用一个非函数对象(nonfunction)或试图选择null的属性。

 

但是在大多数情况下,JavaScript不会抛出一个错误,而是按照多种多样的自动转换协议将值强制转换为期望的类型。例如,算术运算符-、*、/和%在计算之前都会尝试将其参数转换为数字。而运算符+更为微妙,因为它既重载了数字相加,又重载了字符串连接操作。具体是数字相加还是字符串连接,这取决于其参数的类型。

 

接下来,合并一个数字和一个字符串会发生什么呢?JavaScript打破了这一束缚,它更偏爱字符串,将数字转换为字符串。

 

类似这样的混合表达式有时令人困惑,因为 JavaScript 对操作顺序是敏感的。例如,表达式:

 

由于加法运算是自左结合的(即左结合律),因此,它等同于下面的表达式:

 

与此相反,表达式:

 

的计算结果为字符串“123”。左结合律相当于是将表达式左侧的加法运算包裹在括号中。

 

位运算符不仅会将操作数转换为数字,而且还会将操作数转换为32位整数(表示的数字的子集)。我们在第2条已经讨论过。这些运算符包括位算术运算符(~、&、^和|)以及移位运算符(<<、>>和>>>)。

这些强制转换十分方便。例如,来自用户输入、文本文件或者网络流的字符串都将被自动转换。

 

但是强制转换也会隐藏错误。结果为null的变量在算术运算中不会导致失败,而是被隐式地转换为0;一个未定义的变量将被转换为特殊的浮点数值NaN(自相矛盾地命名为“not a number”。谴责IEEE浮点数标准!)。这些强制转换不是立即抛出一个异常,而是继续运算,往往导致一些令人困惑和不可预测的结果。无奈的是,即便是测试NaN值也是异常困难的。这有两个原因。第一,JavaScript遵循了IEEE浮点数标准令人头痛的要求——NaN不等于其本身。因此,测试一个值是否等于NaN根本行不通。

 

另外,标准的库函数isNaN也不是很可靠,因为它带有自己的隐式强制转换,在测试其参数之前,会将参数转换为数字(isNaN函数的一个更精确的名称可能是coercesToNaN)。如果你已经知道一个值是数字,你可以使用isNaN函数测试它是否是NaN。

 

但是对于其他绝对不是NaN,但会被强制转换为NaN的值,使用isNaN方法是无法区分的。

 

幸运的是,有一个既简单又可靠的习惯用法用于测试NaN,虽然稍微有点不直观。由于NaN是JavaScript中唯一一个不等于其自身的值,因此,你可以随时通过检查一个值是否等于其自身的方式来测试该值是否是NaN。


 

其实测试一个值是否与其自身相等是非常简洁的,通常没有必要借助于一个辅助函数,但关键在于识别和理解。

隐式的强制转换使得调试一个出问题的程序变得令人异常沮丧,因为它掩盖了错误,使错误更难以诊断。当一个计算出了问题,最好的调试方式是检查这个计算的中间结果,回到出错前的“最后一点”。在那里,你可以检查每个操作的参数,查看错误类型的参数。根据错误的不同,它可能是一个逻辑错误(如使用了错误的算术运算符),也可能是一个类型错误(如传入了一个undefined的值而不是数字)。

对象也可以被强制转换为原始值。最常见的用法是转换为字符串。

 

对象通过隐式地调用其自身的toString方法转换为字符串。你可以调用对象的toString方法进行测试。

 

类似地,对象也可以通过其valueOf方法转换为数字。通过定义类似下面这些方法,你可以控制对象的类型转换。

 
再一次,当你认识到运算符+被重载来实现字符串连接和加法时,事情变得棘手起来。特别是,当一个对象同时包含toString和valueOf方法时,运算符+应该调用哪个方法并不明显——做字符串连接还是加法应该根据参数的类型,但是存在隐式的强制转换,因此类型并不是显而易见!JavaScript通过盲目地选择valueOf方法而不是toString方法来解决这种含糊的情况。但是,这就意味着如果有人打算对一个对象执行字符串连接操作,那么产生的行为将会出乎意料。

 

这个例子的说明,valueOf方法才真正是为那些代表数值的对象(如Number对象)而设计的。对于这些对象,toString和valueOf方法应返回一致的结果(相同数字的字符串或数值表示),因此,不管是对象的连接还是对象的相加,重载的运算符+总是一致的行为。一般情况下,字符串的强制转换远比数字的强制转换更常见、更有用。最好避免使用valueOf方法,除非对象的确是一个数字的抽象,并且obj.toString()能产生一个obj.valueOf()的字符串表示。

最后一种强制转换有时称为真值运算(truthiness)。if、||和&&等运算符逻辑上需要布尔值作为操作参数,但实际上可以接受任何值。JavaScript按照简单的隐式强制转换规则将值解释为布尔值。大多数的JavaScript值都为真值(truthy),也就是能隐式地转换为true。对于字符串和数字以外的其他对象,真值运算不会隐式调用任何强制转换方法。JavaScript中有7个假值:false、0、-0、""、NaN、null和undefined。其他所有的值都为真值。由于数字和字符串可能为假值,因此,使用真值运算检查函数参数或者对象属性是否已定义不是绝对安全的。例如,一个带有默认值的接受可选参数的函数:


 

第54条将讨论针对库和API设计的真值运算测试的影响。

提示

类型错误可能被隐式的强制转换所隐藏。

重载的运算符+是进行加法运算还是字符串连接操作取决于其参数类型。

对象通过valueOf方法强制转换为数字,通过toString方法强制转换为字符串。

具有valueOf方法的对象应该实现toString方法,返回一个valueOf方法产生的数字的字符串表示。

测试一个值是否为未定义的值,应该使用typeof或者与undefined进行比较而不是使用真值运算。

点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:第2条:理解JavaScript的浮点数
下一篇:第4条:原始类型优于封装对象
相关文章
图文推荐
3.12 本章小结
3.10 添加新函数
3.9 递归
3.8 闭包
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站