JavaScript是否具有“短路”评估?


101

我想知道JavaScript是否具有C#中的&&运算符之类的“短路”评估。如果没有,我想知道是否有一种合理的解决方法可以采用。


2
别客气。我已添加https://www.google.com/search?q=site:stackoverflow.com+%s为搜索快捷方式(Chrome / Firefox),以加快搜索速度。
罗伯W


Answers:


118

是的,JavaScript具有“短路”评估。

if (true == true || foo.foo){
    // Passes, no errors because foo isn't defined.
}

现场演示

if (false && foo.foo){
    // Passes, no errors because foo isn't defined.
}

现场演示


8
短路是JS的标准吗?
GibboK 2012年

1
谢谢gdoron,请帮助我理解...在C#中,我也有&这样的二进制运算符,因此两个操作数都必须为true才能传递,而在C中使用&&代替
GibboK 2012年

1
@GibboK。然后,显然,Short-circuit该逻辑运算符不可能存在。自己尝试一下。使用我的演示。
gdoron在2012年

2
@GibboK:查看此操作员参考。是的,JS中也有一个二进制AND运算符。
Bergi 2012年

5
@GibboK。是的,在标准中!但是很好的评论,就像在javascript实现中的JIT-compiling-magic时代一样,人们确实确实想知道某些东西是“标准”还是潜在地受实现影响。使用二进制逻辑运算符评估条件语句的方式和(短曲线
humanityANDpeace

23

这个答案详细介绍了如何 如果您正在寻找一种快速定义并且已经了解短路的工作原理,则可以在JavaScript中使用所有陷阱以及相关主题(例如运算符优先级)在JavaScript中工作,我建议您检查其他答案。


到目前为止,我们(所认为的)所知道的是:

首先,让我们在if()块中检查我们熟悉的行为,在其中&&检查两件事是否是true

if (true && true) {
   console.log('bar');
} 

现在,您的第一个直觉可能是:“是的,很简单,如果代码同时执行expr1expr2被评估为true'

好吧,是的,不是。您在技术上是正确的,这就是您所描述的行为,但这并不是评估代码的方式,我们需要更深入地研究才能完全理解。


&&和的确切||解释是:

是时候看起来“在 引擎”。让我们考虑这个实际示例:

function sanitise(x) {
  if (isNaN(x)) {
    return NaN;
  }
  return x;
}

let userinput = 0xFF; // as an example
const res = sanitise(userinput) && userinput + 5

console.log(res);

结果是260..但是为什么呢?为了获得答案,我们需要了解短路评估的工作原理。

通过MDN定义&&操作符将在expr1 && expr2以下执行:

如果expr1可以转换为true,则返回expr2; 否则,返回expr1

因此,这意味着在我们的实际示例中,将const res通过以下方式评估:

  1. 调用expr1-sanitise(0xFF)
  2. 0xFF 是250的有效十六进制数,否则我将返回 NaN
  3. expr1返回“truthy”值,执行时间expr2 (否则我会停止为NaN是falsy)
  4. 由于userinput是truthy(数字),我可以添加+5到它
  • “ Truthy”表示可以将表达式评估为true。这是真实虚假 表达的清单 。

因此,在这里,我们可以通过简单地使用运算符来避免其他if块并进一步isNaN检查&&


实际运作方式:

到现在为止,我们至少应该了解一下 操作员的工作。通用规则是:

  • (some falsy expression) && expr 将评估为虚假的表达
  • (some truthy expression) || expr 将评估为真实表达

以下是进一步理解的一些示例:

function a() { console.log('a'); return false; }
function b() { console.log('b'); return true; }

if ( a() && b() ){
     console.log('foobar'); 
}

//Evaluates a() as false, stops execution.

function a() { console.log('a'); return false; }
function b() { console.log('b'); return true; }

if ( a() || b() ){
     console.log('foobar'); 
}

/* 1. Evaluates a() as false
   2. So it should execute expr2, which is `b()`
   3. b() returned as true, executing statement `console.log('foobar');`
*/


最后一件烦人但非常重要的事情[操作员优先]:

很好,希望您能掌握一切!我们需要知道的最后一件事是关于运算符优先级的规则,即:

  • &&运营商总是先于执行的||操作。

考虑以下示例:

function a() { console.log('a'); return true;}
function b() { console.log('b'); return false;}
function c() { console.log('c'); return false;}

console.log(a() || b() && c());

// returns a() and stops execution

这将以的形式返回,也许会使某些人感到困惑a()。原因很简单,只是我们的目光在欺骗我们,因为我们习惯于从左到右阅读。让我们拿走console.log()和不拿走,只专注于评估

true || false && false

现在,您可以解决这个问题:

  1. 我们说过&&运算符具有优先权,因此它首先被评估。为了帮助我们更好地想象评估,请考虑一下定义

    expr1 && expr2

    哪里:

    • expr2false
    • expr1true || false
  2. 因此这是棘手的部分,现在true || false进行评估(的expr1-左侧&&)。

    • 给定||运算符,如果expr1 || expr2in expr1评估为“真”,则停止执行,然后expr1执行并停止执行代码。
  3. 返回值是 true

嗯..这很棘手,这都是因为很少有奇怪的规则和语义。但是请记住,您总是可以使用()- 来转义运算符的优先级,就像在数学中一样

function a() { console.log('a'); return true;}
function b() { console.log('b'); return false;}
function c() { console.log('c'); return false;}

console.log((a() || b()) && c());

/* 1. The () escape && operator precedence
   2. a() is evaluated as false, so expr2 (c()) to be executed
   3. c()  
*/


我会1)不使用“编译器”一词。“引擎”更准确。2)不要谈论expr1expr2 / condition1或其他任何东西,这只是令人困惑。确定一个,您也可以引入局部变量,例如。const expr1 = true; if(expr1 && ...)
乔纳斯·威尔姆斯

@JonasWilms感谢您的输入,并相应地修改了答案。
塞缪尔·赫拉

1
仍然不能直接回答所问的问题。
凯文B

7
这是我所见过的最好的“最好的答案,不能明确回答问题” ……
Gerardo Furtado

1
这是带有正确解释的正确答案,应该被标记为已接受,并且要比目前更多支持!
亚历山大·金
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.