JavaScript 有“短路”评估吗?

IT技术 javascript short-circuiting
2021-01-12 01:54:08

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

3个回答

是的,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.
}

现场演示

@吉博克。是的标准!但是好的评论,就像在 javascript 实现中的 JIT-compiling-magic 时代一样,人们确实想知道某些东西是否是“标准”,或者是否可能受到实现的影响。使用二元逻辑运算符评估条件语句的方式和 (short- curcuit ) 是标准行为ecma-international.org/ecma-262/5.1/#sec-11.11
2021-03-12 01:54:08
@吉博克。那么,显然,不能有一个Short-circuit带有该逻辑运算符的。自己试试吧。使用我的演示。
2021-03-14 01:54:08
那么短路它是JS中的标准吗?
2021-03-20 01:54:08
@GibboK:查看此操作员参考是的,JS 中也有一个二元 AND 运算符。
2021-03-22 01:54:08
谢谢gdoron,请帮助我理解......在C#中我也有像&这样的二元运算符,所以两个操作数都必须为真才能通过,而不是在C中使用&&
2021-04-04 01:54:08

这个答案非常详细地介绍了如何 在 JavaScript 中工作,具有所有问题以及相关主题,例如运算符优先级,如果您正在寻找快速定义并且已经了解短路的工作原理,我建议您查看其他答案。


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

首先让我们检查一下我们都熟悉的行为,在if()内部,我们&&用来检查两件事是否是true

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

现在,你的第一react可能是说:“是啊,很简单,代码执行,如果双方的陈述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是真的(一个数字),我可以添加+5
  • “Truthy”意味着表达式可以被评估为真。这是一个truthyfalsy 表达式的列表

因此,在这里,我们能够通过简单地使用运算符来避免额外的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- 的左侧&&)。

    • ||如果expr1 || expr2inexpr1评估为真,运算符停止执行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()  
*/

这是我见过的最好的“没有明确回答问题的好答案” ......
2021-03-18 01:54:08
这是带有深刻解释的正确答案,应该被标记为已接受和比目前更多的投票!
2021-03-18 01:54:08
仍然没有直接回答所提出的问题。
2021-03-19 01:54:08
@JonasWilms 感谢您的输入,并相应地修改了答案。
2021-03-20 01:54:08
我会 1) 不使用“编译器”这个词。“引擎”更准确。2)不要谈论expr1expr2condition1或其他什么,那只是令人困惑。决定一个,你也可以引入局部变量,例如。const expr1 = true; if(expr1 && ...)
2021-03-22 01:54:08

这个想法是从左到右读取逻辑表达式,如果左条件的值足以得到总值,则不会处理和评估右条件。一些非常简单的例子:

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 中,您将看到模态窗口要求您进行选择,因为前两者取决于正确的部分(即您的选择),而后两者 - 无论聚合这些表达式的值不取决于您的选择——因为首先读取左条件。因此,根据要首先处理的条件从左到右放置条件非常重要。