133

我想知道 JavaScript 是否具有像 C# 中的 && 运算符这样的“短路”评估。如果没有,我想知道是否有一种可行的解决方法。

4

3 回答 3

150

Yes, JavaScript has "short-circuit" evaluation.

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

Live DEMO

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

Live DEMO

于 2012-09-23T17:36:17.600 回答
48

这个答案非常详细地介绍了在 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 定义&&运算符 inexpr1 && expr2执行如下:

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

所以这意味着,在我们的实际示例中,const res按以下方式评估 :

  1. 调用expr1-sanitise(0xFF)
  2. 0xFF是 250 的有效十六进制数,否则我会返回NaN
  3. 返回一个“expr1真实”值,执行时间expr2 (否则我会因为NaN错误而停止)
  4. 既然userinput是真实的(一个数字),我可以添加+5
  • “真”意味着表达式可以被评估为真。这是一个 真实虚假 表达的列表。

所以在这里,我们能够通过简单地使用操作符来避免额外的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

这将返回 as ,可能会让一些人感到困惑 as a()。原因很简单,只是我们的视力有点欺骗我们,因为我们习惯于从左到右阅读。让我们把console.log()重点放在评估上

true || false && false

现在来解决这个问题:

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

    expr1 && expr2
    

    在哪里:

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

    • 如果in评估为真,则||运算符停止执行,则执行并且代码执行停止。expr1 || expr2expr1expr1
  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()  
*/

于 2019-05-09T10:23:22.657 回答
1

思路是从左到右读取逻辑表达式,如果左边条件的值足够得到总值,右边的条件就不会被处理和评估。一些非常简单的例子:

function test() {
    const caseNumber = document.querySelector('#sel').value;
    const userChoice = () => confirm('Press OK or Cancel');
    if (caseNumber === '1') {
        console.log (1 === 1 || userChoice());
    } else if (caseNumber === '2') {
        console.log (1 === 2 && userChoice());
    } else if (caseNumber === '3') {
        console.log (1 === 2 || userChoice());
    } else if (caseNumber === '4') {
        console.log (1 === 1 && userChoice());
    } else if (caseNumber === '5') {
        console.log (userChoice() || 1 === 1);
    } else if (caseNumber === '6') {
        console.log (userChoice() && 1 === 2);
    }
}
<label for="sel">Select a number of a test case and press "RUN!":</label>
<br><select id="sel">
  <option value="">Unselected</option>
  <option value="1">Case 1</option>
  <option value="2">Case 2</option>
  <option value="3">Case 3</option>
  <option value="4">Case 4</option>
  <option value="5">Case 5</option>
  <option value="6">Case 6</option>
</select>
<button onclick="test()">RUN!</button>

上面的前两种情况将分别打印到控制台结果truefalse您甚至不会看到要求您按“确定”或“取消”的模式窗口,因为左侧条件足以定义总结果。相反,在案例 3-6 中,您将看到模态窗口询问您的选择,因为前两个取决于正确的部分(即您的选择),而后两个 - 不管聚合的事实如何这些表达式的值不取决于您的选择——因为首先读取左条件。因此,重要的是根据您希望首先处理的条件从左到右放置条件。

于 2020-11-09T12:19:07.137 回答