[英]Why are logical operators in JavaScript left associative?
邏輯AND和OR運算符是JavaScript中唯一的延遲運算符以及三元條件運算符 。 他們使用以下規則進行短路評估測試:
false && anything === false
true || anything === true
這與在Haskell中實現的方式相同:
(&&) :: Bool -> Bool -> Bool
False && _ = False
True && x = x
(||) :: Bool -> Bool -> Bool
True || _ = True
False || x = x
但是根據MDN,JavaScript中的邏輯運算符是左關聯的 。 這是違反直覺的。 在我的拙見中,他們應該是正確的聯想。 Haskell做對了。 Haskell中的邏輯運算符是正確的關聯:
infixr 3 &&
infixr 2 ||
考慮Haskell中的以下表達式:
False && True && True && True
因為&&
在Haskell中是正確關聯的,所以上面的表達式相當於:
False && (True && (True && True))
因此,表達式(True && (True && True))
評估結果並不重要。 由於第一個False
,整個表達式在一個步驟中減少為False
。
現在考慮如果&&
是關聯的會發生什么。 表達式相當於:
((False && True) && True) && True
現在需要3次減少來評估整個表達式:
((False && True) && True) && True
(False && True) && True
False && True
False
正如您所看到的,邏輯運算符更適合於正確關聯。 這讓我想到了我的實際問題:
為什么JavaScript中的邏輯運算符是關聯的? ECMAScript規范對此有何看法? JavaScript中的邏輯運算符實際上是正確的關聯嗎? MDN文檔是否有關於邏輯運算符的關聯性的錯誤信息?
編輯:根據規范,邏輯運算符是左關聯的:
LogicalANDExpression = BitwiseORExpression
| LogicalANDExpression && BitwiseORExpression
LogicalORExpression = LogicalANDExpression
| LogicalORExpression || LogicalANDExpression
對於任何體面的編譯器,這些運算符的選擇關聯性幾乎是無關緊要的,輸出的代碼無論如何都是相同的。 是的,解析樹是不同的,但不需要發出的代碼。
在我所知道的C系列的所有語言中(Javascript也屬於這些語言),邏輯運算符是左關聯的。 所以真正的問題是,為什么類C語言將邏輯運算符定義為左關聯? 由於所選擇的關聯性是無關緊要的(就語義和效率而言),我的懷疑是選擇了最“自然”(如“大多數其他運營商使用的”)相關性,盡管我不喜歡有任何消息來支持我的說法。 其他可能的解釋是左關聯運算符使用LALR解析器來解析更少的堆棧空間(現在這不是一個大問題,但可能在C出現時回來)。
考慮以下代碼:
console.log( true || (false && true) ); // right associative (implicit)
console.log( (true || false) && true ); // left associative (Javascript)
這兩個例子實際上都會返回相同的結果,但這並不是擔心運算符關聯性的原因。 它與此相關的原因是因為邏輯運算符決定其結果的獨特方式。 即使所有排列最終都得出相同的最終結論,計算的順序也會發生變化,這對您的代碼會產生重大影響。
那么,現在考慮一下:
var name = "";
// ----------------------------------------
// Right associative
// ----------------------------------------
name = "albert einstein";
console.log("RIGHT : %s : %s", true || (false && updateName()), name);
// ----------------------------------------
// Left Associative
// ----------------------------------------
name = "albert einstein";
console.log("LEFT : %s : %s", (true || false) && updateName(), name);
function updateName() {
name = "charles manson";
return true;
}
輸出是:
RIGHT : true : albert einstein
LEFT : true : charles manson
兩個表達式都返回true,但只有左關聯版本必須調用updateName()才能返回答案。 正確的關聯版本是不同的。 它只計算(false && updateName())
的第一個參數,因為第二個參數不能將false
更改為true
。
請記住這兩點:
請注意,上述兩點都不會改變單個表達式的解釋方式,只會改變復合表達式的解釋方式。 相關性發生在更高的層次上。
運算符關聯性以及運算符優先級對語言的行為方式產生巨大影響。 了解這些差異對於能夠使用和快速掌握不同編程語言中的功能差異至關重要。 你肯定會對我的問題豎起大拇指,我希望這會澄清一些事情。 照顧自己。
簡單回答:想象一下var i = null; if(i == null || i.getSomeValue())...當不是左關聯時,將首先評估第二個測試,給出異常。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.