核心要点
false
、undefined
、null
、0
、""
(空字符串)和 NaN
(非数字)。所有其他值都被视为真值。typeof
进行显式测试始终安全,但使用自动类型转换有利于文件大小的考虑,因为较小的文件加载速度更快且带宽使用更少。但是,理解编程语言如何处理类型转换对于避免意外结果至关重要。JavaScript 中有一些表达式很常见,但一些编程纯粹主义者会告诉你它们从来都不是好主意。这些表达式的共同点是它们依赖于自动类型转换——JavaScript 的核心功能,根据情况和你的观点,它既是优势也是劣势。
因此,在本文中,我想特别关注这两个表达式,并考虑它们在哪些情况下是好主意,哪些情况下不是好主意。
第一个表达式是一个简单的 if()
条件:
<code class="language-javascript">if (foo) { }</code>
第二个表达式是一个带有可选值的变量赋值:
<code class="language-javascript">var x = foo || bar;</code>
如果这两个例子中的 foo
和 bar
都是布尔值,那么表达式很简单:如果 foo
为真,则第一个条件通过;如果 foo
为真,则第二个表达式将 foo
赋值给 x
,否则将 bar
赋值给 x
。
但是,如果它们不是简单的布尔值——如果 foo
是一个对象、一个字符串或 undefined
怎么办?如果 foo
和 bar
是不同的数据类型怎么办?要理解这些表达式将如何计算,我们需要了解 JavaScript 如何在数据类型之间自动转换。
自动类型转换
JavaScript 是一种“松散类型”语言,这意味着每当运算符或语句期望特定数据类型时,JavaScript 都会自动将数据转换为该类型。第一个例子中的 if()
语句期望一个布尔值,因此括号中定义的任何内容都将转换为布尔值。while()
和 do...while()
语句也是如此。
根据此类转换的结果(即真或假),JavaScript 值通常被称为“真值”或“假值”。最简单的理解方法是这样的:除非已知值为假值,否则该值为真值;实际上,只有 六个 假值:
false
(当然!)undefined
null
0
(数字零)""
(空字符串)NaN
(非数字)值得注意的例外是“0”(字符串零)和所有类型的对象——它们是真值——这包括 所有 原生构造函数,这意味着 new Boolean(false)
计算结果为真!(有点令人困惑,但在实践中你永远不需要那样创建原生值。)
注意:比较两个假值并不总是会产生你可能期望的结果,例如(null != false
),即使两者都是假值。有一些相当复杂的算法决定了相等性评估的工作方式,讨论它们超出了本文的范围。但如果你对细节感兴趣,可以查看 ECMAScript 5.1 中的抽象相等比较算法。
条件快捷方式
我在开头向你展示的 if()
示例将其表达式转换为布尔值,由于对象总是计算为真而 null
计算为假,因此我们可以使用这样的条件来测试 DOM 元素的存在:
<code class="language-javascript">if (foo) { }</code>
在处理 DOM 元素时,这始终可靠地工作,因为 DOM 规范要求不存在的元素返回 null
。
但是,其他情况并非如此明确,例如这个例子:
<code class="language-javascript">var x = foo || bar;</code>
这样的条件经常用于表示 “如果 foo 参数已定义”,但是有几种情况会导致失败——即 foo
是假值的情况。因此,例如,如果它是布尔值 false
或空字符串,则不会执行条件代码,即使 foo
已 定义。
我们想要的是这个:
<code class="language-javascript">var element = document.getElementById("whatever"); if (element) { // 元素存在 } else { // 元素不存在 }</code>
未定义的参数(和其他变量)的数据类型为“undefined
”。因此,我们可以使用 typeof
比较器来测试参数的数据类型,然后如果 foo
完全定义,则条件将始终通过。当然,if()
表达式仍在计算布尔值,但它计算的布尔值是该 typeof
表达式的 结果。
赋值快捷方式
我在开头向你展示的第二个示例使用逻辑运算符来确定应将两个值中的哪个值赋给变量:
<code class="language-javascript">function doStuff(foo) { if (foo) { ... } }</code>
逻辑运算符不 返回 布尔值,但它们确实仍然 期望 布尔值,因此转换和计算会在内部发生。如果 foo
计算结果为真,则返回 foo
的 值,否则返回 bar
的值。这非常有用。
这个表达式通常在事件处理函数中看到,它用于根据支持的模型定义事件参数:
<code class="language-javascript">if (foo) { }</code>
因此,e
计算为布尔值,如果支持事件参数模型,则该布尔值为真值(事件对象),否则为假值(undefined
);如果它是真值,则返回 e
,否则返回 window.event
。
同样类型的表达式也常用于分配事件属性,通过评估每个可能性来查找支持的属性:
<code class="language-javascript">var x = foo || bar;</code>
因此,这些引用中的每一个都依次(从左到右)进行评估,并且第一个计算结果为真的将被返回。第一种情况处理标准模型,第二种情况用于 Internet Explorer,而第三种情况用于 Internet Explorer,当事件可能在窗口对象(没有 srcElement
属性)上触发时。
但是,这种表达式同样容易失败,在不知道数据真值性的情况下。例如,另一个常见的用例是为可选参数定义默认值,但这不好:
<code class="language-javascript">var element = document.getElementById("whatever"); if (element) { // 元素存在 } else { // 元素不存在 }</code>
现在,如果你确定 foo
总是 是字符串或 undefined
,并且假设空字符串应被视为 undefined
,那么该表达式是安全的。但如果不是,则需要将其重新定义为更精确的内容,例如:
<code class="language-javascript">function doStuff(foo) { if (foo) { ... } }</code>
通过针对“字符串”测试类型,我们可以处理多种情况——foo
未定义的情况,以及将其错误定义为非字符串值的情况。在这种情况下,我们还允许空字符串作为有效输入,但如果我们想排除空字符串,则必须添加第二个条件:
<code class="language-javascript">function doStuff(foo) { if (typeof foo != "undefined") { ... } }</code>
还有其他一些令人惊讶的微妙情况可能会导致问题。例如,我们可能有一个日期函数,它创建 unix 时间戳,除非可选地定义输入时间戳:
<code class="language-javascript">var x = foo || bar;</code>
如果输入为 0,则会失败——因为零是假值,但它也是有效的时间戳。
一般原则
从所有这些中吸取的一般教训很简单——考虑类型转换将如何影响评估,并注意不要陷入我们遇到的陷阱。经过适当的注意和关注,你仍然可以利用自动类型转换,在适当的情况下缩短条件和逻辑表达式。
但这确实提出了一个问题——如果我们知道使用 typeof
进行显式测试始终安全,而依赖自动类型转换有时并不安全——那么为什么不 始终 明确呢?当然,如果偏好较短语法的原因仅仅是因为它键入速度更快,那么这是一个懒惰和草率的原因。
但事实是,JavaScript 通常通过公共网络运行,在这种情况下文件大小很重要。较小的文件加载速度更快,并且带宽使用更少,小的语法快捷方式确实可以累积。
利用较短的表达式本身并不是一种优化,它只是一种编程风格,它充分利用了语言特性。
(此处省略了原文中的常见问题解答部分,因为该部分内容与文章主题关联性较弱,并且篇幅较长,不适合在伪原创中保留。)
以上是现实世界中的自动类型转换的详细内容。更多信息请关注PHP中文网其他相关文章!