自动类型转换

在引言中,我提到 JavaScript 会尽可能接受几乎所有你给他的程序,甚至是那些做些奇怪事情的程序。 以下表达式很好地证明了这一点:

  1. console.log(8 * null)
  2. // → 0
  3. console.log("5" - 1)
  4. // → 4
  5. console.log("5" + 1)
  6. // → 51
  7. console.log("five" * 2)
  8. // → NaN
  9. console.log(false == 0)
  10. // → true

当运算符应用于类型“错误”的值时,JavaScript 会悄悄地将该值转换为所需的类型,并使用一组通常不是你想要或期望的规则。 这称为类型转换。 第一个表达式中的null变为0,第二个表达式中的"5"变为5(从字符串到数字)。 然而在第三个表达式中,+在数字加法之前尝试字符串连接,所以1被转换为"1"(从数字到字符串)。

当某些不能明显映射为数字的东西(如"five"undefined)转换为数字时,你会得到值NaNNaN进一步的算术运算会产生NaN,所以如果你发现自己在一个意想不到的地方得到了它,需要寻找意外的类型转换。

当相同类型的值之间使用==符号进行比较时,其运算结果很好预测:除了NaN这种情况,只要两个值相同,则返回true。但如果类型不同,JavaScript 则会使用一套复杂难懂的规则来确定输出结果。在绝大多数情况下,JavaScript 只是将其中一个值转换成另一个值的类型。但如果运算符两侧存在nullundefined,那么只有两侧均为nullundefined时结果才为true

  1. console.log(null == undefined);
  2. // → true
  3. console.log(null == 0);
  4. // → false

这种行为通常很有用。 当你想测试一个值是否具有真值而不是nullundefined时,你可以用==(或!=)运算符将它与null进行比较。

但是如果你想测试某些东西是否严格为“false”呢? 字符串和数字转换为布尔值的规则表明,0NaN和空字符串("")计为false,而其他所有值都计为true。 因此,像'0 == false'"" == false这样的表达式也是真的。 当你不希望发生自动类型转换时,还有两个额外的运算符:===!==。 第一个测试是否严格等于另一个值,第二个测试它是否不严格相等。 所以"" === false如预期那样是错误的。

我建议使用三字符比较运算符来防止意外类型转换的发生,避免作茧自缚。但如果比较运算符两侧的值类型是相同的,那么使用较短的运算符也没有问题。

逻辑运算符的短路特性

逻辑运算符&&||以一种特殊的方式处理不同类型的值。 他们会将其左侧的值转换为布尔型,来决定要做什么,但根据运算符和转换结果,它们将返回原始的左侧值或右侧值。

例如,当左侧值可以转换为true时,||运算符会返回它,否则返回右侧值。 当值为布尔值时,这具有预期的效果,并且对其他类型的值做类似的操作。

  1. console.log(null || "user")
  2. // → user
  3. console.log("Agnes" || "user")
  4. // → Agnes

我们可以此功能用作回落到默认值的方式。 如果你的一个值可能是空的,你可以把||和备选值放在它之后。 如果初始值可以转换为false,那么你将得到备选值。

&&运算符工作方式与其相似但不相同。当左侧的值可以被转换成false时,&&运算符会返回左侧值,否则返回右侧值。

这两个运算符的另一个重要特性是,只在必要时求解其右侧的部分。 在true || X的情况下,不管X是什么 - 即使它是一个执行某些恶意操作的程序片段,结果都是true,并且X永远不会求值。 false && X也是一样,它是false的,并且忽略X。 这称为短路求值。

条件运算符以类似的方式工作。 在第二个和第三个值中,只有被选中的值才会求值。