附注栏提到过 && 和 || 运算符的“短路”(short circuiting)特性。下面我们将
对此进行详细介绍。
对 && 和 || 来说,如果从左边的操作数能够得出结果,就可以忽略右边的操作数。我们将 这种现象称为“短路”(即执行最短路径)。
以a && b为例,如果a是一个假值,足以决定&&的结果,就没有必要再判断b的值。同 样对于 a || b,如果 a 是一个真值,也足以决定 || 的结果,也就没有必要再判断 b 的值。
“短路”很方便,也很常用,如:
function doSomething(opts) {
if (opts && opts.cool) {
// .. }
}
opts && opts.cool中的opts条件判断如同一道安全保护,因为如果opts未赋值(或者 不是一个对象),表达式 opts.cool 会出错。通过使用短路特性,opts 条件判断未通过时 opts.cool 就不会执行,也就不会产生错误!
|| 运算符也一样:
function doSomething(opts) {
if (opts.cache || primeCache()) {
// .. }
}
这里首先判断 opts.cache 是否存在,如果是则无需调用 primeCache() 函数,这样可以避
免执行不必要的代码。
更强的绑定
回顾一下前面多个运算符串联在一起的例子:
a && b || c ? c || b ? a : c && b : a
其中 ? : 运算符的优先级比 && 和 || 高还是低呢?执行顺序是这样? a && b || (c ? c || (b ? a : c) && b : a)
还是这样?
(a && b || c) ? (c || b) ? a : (c && b) : a
答案是后者。因为 && 运算符的优先级高于 ||,而 || 的优先级又高于 ? :。
因此表达式(a && b || c)先于包含它的? :运算符执行。另一种说法是&&和||比? :的 绑定更强。反过来,如果 c ? c… 的绑定更强,执行顺序就会变成 a && b || (c ? c…)。
关联
&&和||运算符先于? :执行,那么如果多个相同优先级的运算符同时出现,又该如何处
理呢?它们的执行顺序是从左到右还是从右到左? 一般说来,运算符的关联(associativity)不是从左到右就是从右到左,这取决于组合
(grouping)是从左开始还是从右开始。
请注意:关联和执行顺序不是一回事。 但它为什么又和执行顺序相关呢?原因是表达式可能会产生副作用,比如函数调用:
var a = foo() && bar();
这里 foo() 首先执行,它的返回结果决定了 bar() 是否执行。所以如果 bar() 在 foo() 之
前执行,整个结果会完全不同。
这里遵循从左到右的顺序(JavaScript 的默认执行顺序),与 && 的关联无关。因为上例中只
有一个 && 运算符,所以不涉及组合和关联。
而a && b && c这样的表达式就涉及组合(隐式),这意味着a && b或b && c会先执行。
从技术角度来说,因为&&运算符是左关联(||也是),所以a && b && c会被处理为(a && b) && c。不过右关联 a && (b && c) 的结果也一样。
如果&&是右关联的话会被处理为a && (b && c)。但这并不意味着c会在b 之前执行。右关联不是指从右往左执行,而是指从右往左组合。任何时候, 不论是组合还是关联,严格的执行顺序都应该是从左到右,a,b,然后 c。
所以,&& 和 || 运算符是不是左关联这个问题本身并不重要,只要对此有一个准确的定义
即可。 但情况并非总是这样。一些运算符在左关联和右关联时的表现截然不同。 比如 ? :(即三元运算符或者条件运算符):
a ? b : c ? d : e;
? : 是右关联,它的组合顺序是以下哪一种呢?
• a ? b : (c ? d : e)
• (a ? b : c) ? d : e
答案是a ? b : (c ? d : e)。和&&以及||运算符不同,右关联在这里会影响返回结果, 因为 (a ? b : c) ? d : e 对有些值(并非所有值)的处理方式会有所不同。
举个例子:
true ? false : true ? true : true;
true ? false : (true ? true : true);
(true ? false : true) ? true : true;
// false
// false
// true
在某些情况下,返回的结果没有区别,但其中却有十分微妙的差别。例如:
true ? false : true ? true : false;
true ? false : (true ? true : false);
(true ? false : true) ? true : false;
// false
// false
// false
这里返回的结果一样,运算符组合看似没起什么作用。然而实际情况是:
var a = true, b = false, c = true, d = true, e = false; a ? b : (c ? d : e); // false, 执行 a 和 b
(a ? b : c) ? d : e; // false, 执行 a, b 和 e
这里我们可以看出,? : 是右关联,并且它的组合方式会影响返回结果。 另一个右关联(组合)的例子是 = 运算符。本章前面介绍过一个串联赋值的例子:
var a, b, c;
a = b = c = 42;
它首先执行 c = 42,然后是 b = …,最后是 a = …。因为是右关联,所以它实际上是这样 来处理的:a = (b = (c = 42))。