自动类型转换
在引言中,我提到 JavaScript 会尽可能接受几乎所有你给他的程序,甚至是那些做些奇怪事情的程序。 以下表达式很好地证明了这一点:
console.log(8 * null)
// → 0
console.log("5" - 1)
// → 4
console.log("5" + 1)
// → 51
console.log("five" * 2)
// → NaN
console.log(false == 0)
// → true
当运算符应用于类型“错误”的值时,JavaScript 会悄悄地将该值转换为所需的类型,并使用一组通常不是你想要或期望的规则。 这称为类型转换。 第一个表达式中的null
变为0
,第二个表达式中的"5"
变为5
(从字符串到数字)。 然而在第三个表达式中,+
在数字加法之前尝试字符串连接,所以1
被转换为"1"
(从数字到字符串)。
当某些不能明显映射为数字的东西(如"five"
或undefined
)转换为数字时,你会得到值NaN
。 NaN
进一步的算术运算会产生NaN
,所以如果你发现自己在一个意想不到的地方得到了它,需要寻找意外的类型转换。
当相同类型的值之间使用==
符号进行比较时,其运算结果很好预测:除了NaN
这种情况,只要两个值相同,则返回true
。但如果类型不同,JavaScript 则会使用一套复杂难懂的规则来确定输出结果。在绝大多数情况下,JavaScript 只是将其中一个值转换成另一个值的类型。但如果运算符两侧存在null
或undefined
,那么只有两侧均为null
或undefined
时结果才为true
。
console.log(null == undefined);
// → true
console.log(null == 0);
// → false
这种行为通常很有用。 当你想测试一个值是否具有真值而不是null
或undefined
时,你可以用==
(或!=
)运算符将它与null
进行比较。
但是如果你想测试某些东西是否严格为“false”呢? 字符串和数字转换为布尔值的规则表明,0
,NaN
和空字符串(""
)计为false
,而其他所有值都计为true
。 因此,像'0 == false'
和"" == false
这样的表达式也是真的。 当你不希望发生自动类型转换时,还有两个额外的运算符:===
和!==
。 第一个测试是否严格等于另一个值,第二个测试它是否不严格相等。 所以"" === false
如预期那样是错误的。
我建议使用三字符比较运算符来防止意外类型转换的发生,避免作茧自缚。但如果比较运算符两侧的值类型是相同的,那么使用较短的运算符也没有问题。
逻辑运算符的短路特性
逻辑运算符&&
和||
以一种特殊的方式处理不同类型的值。 他们会将其左侧的值转换为布尔型,来决定要做什么,但根据运算符和转换结果,它们将返回原始的左侧值或右侧值。
例如,当左侧值可以转换为true
时,||
运算符会返回它,否则返回右侧值。 当值为布尔值时,这具有预期的效果,并且对其他类型的值做类似的操作。
console.log(null || "user")
// → user
console.log("Agnes" || "user")
// → Agnes
我们可以此功能用作回落到默认值的方式。 如果你的一个值可能是空的,你可以把||
和备选值放在它之后。 如果初始值可以转换为false
,那么你将得到备选值。
&&
运算符工作方式与其相似但不相同。当左侧的值可以被转换成false
时,&&
运算符会返回左侧值,否则返回右侧值。
这两个运算符的另一个重要特性是,只在必要时求解其右侧的部分。 在true || X
的情况下,不管X
是什么 - 即使它是一个执行某些恶意操作的程序片段,结果都是true
,并且X
永远不会求值。 false && X
也是一样,它是false
的,并且忽略X
。 这称为短路求值。
条件运算符以类似的方式工作。 在第二个和第三个值中,只有被选中的值才会求值。