我想知道 JavaScript 是否具有像 C# 中的 && 运算符这样的“短路”评估。如果没有,我想知道是否有一种可行的解决方法。
3 回答
这个答案非常详细地介绍了短路在 JavaScript 中的工作原理,包括所有的问题以及运算符优先级等相关主题,如果您正在寻找一个快速定义并且已经了解短路的工作原理,我会推荐检查其他答案。
到目前为止,我们(认为我们)知道的:
首先让我们检查一下我们都熟悉的行为,在if()
块内部,我们&&
用来检查两件事是否是true
:
if (true && true) {
console.log('bar');
}
现在,您的第一直觉可能会说:'是的,很简单,代码执行语句,如果两者expr1
和expr2
被评估为true
'
嗯,是的,也不是。您在技术上是正确的,这就是您描述的行为,但这并不是代码的评估方式,我们需要更深入地研究才能完全理解。
&&
和究竟是如何||
解释的?:
是时候看看“在javascript引擎的底层”了。让我们考虑这个实际的例子:
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
按以下方式评估 :
- 调用
expr1
-sanitise(0xFF)
0xFF
是 250 的有效十六进制数,否则我会返回NaN
- 返回一个“
expr1
真实”值,执行时间expr2
(否则我会因为NaN
错误而停止) - 既然
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
现在来解决这个问题:
我们说
&&
运算符具有优先级,因此它首先被评估。为了帮助我们更好地想象评估,想想定义expr1 && expr2
在哪里:
expr2
是false
expr1
是true || false
所以这是棘手的部分,现在
true || false
被评估(expr1
- 的左侧&&
)。- 如果in评估为真,则
||
运算符停止执行,则执行并且代码执行停止。expr1 || expr2
expr1
expr1
- 如果in评估为真,则
返回值为
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()
*/
思路是从左到右读取逻辑表达式,如果左边条件的值足够得到总值,右边的条件就不会被处理和评估。一些非常简单的例子:
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>
上面的前两种情况将分别打印到控制台结果true
和false
您甚至不会看到要求您按“确定”或“取消”的模式窗口,因为左侧条件足以定义总结果。相反,在案例 3-6 中,您将看到模态窗口询问您的选择,因为前两个取决于正确的部分(即您的选择),而后两个 - 不管聚合的事实如何这些表达式的值不取决于您的选择——因为首先读取左条件。因此,重要的是根据您希望首先处理的条件从左到右放置条件。