首頁  >  文章  >  web前端  >  逗號運算子的一個令人信服的案例

逗號運算子的一個令人信服的案例

WBOY
WBOY原創
2024-09-07 06:39:021160瀏覽

A Compelling Case for the Comma Operator

逗號運算子是類別C語言(例如JavaScript和C++)中較不為人所知的運算子之一。本質上,它界定了一系列表達式並僅傳回最後一個表達式的結果。

const a = 1;
const b = 2;
const c = 3;
const result = (a, b, c, 4, 5, 6, true);
console.log(result); // true
if (false, true) console.log('hello'); // hello

很自然地會問:什麼時候將多個表達式塞進一行會有用? 此外,即使它有用,為什麼用逗號分隔的表達式序列(在單行)比分號分隔的語句序列(跨多行)更具可讀性和可維護性?我們什麼時候應該選擇其中一種而不是另一種?

這些是我多年來一直努力回答的問題,但現在我想我終於有了答案。在本文中,我針對逗號運算子提出了一個令人信服的案例(坦白說,也許是唯一的案例)。

一個激勵人心的例子

我們先來說一下條件三元運算子。如下圖所示,如果條件為真,則評估值。否則,它評估另一個。這裡強調關鍵字“評估”,因為分支僅在滿足條件時執行。

const result = condition ? value : another;

在大多數情況下,它都是整潔漂亮的。然而,當我們需要在返回條件值之前在分支之間執行更複雜的邏輯時,它就會崩潰。此時,我們訴諸這種不幸的變態:

let result; // Uninitialized! Yikes!
if (condition) {
    // Do some complex stuff in between...
    doSomething();
    // ...
    result = value; // Actual Assignment
} else {
    // Do other complex stuff in between...
    doAnotherThing();
    // ...
    result = another; // Actual Assignment
}
// Hopefully we didn't forget to initialize `result`!

現在這個配方存在很多問題。

  1. 結果一開始是未初始化的。這本質上並不是邪惡的,但避免由於未定義而導致錯誤的一種簡單且經過驗證的方法是始終初始化變數。
  2. 結果的初始化實際上是在分支的底部—與其聲明相分離。
  3. 在條件結束時,我們最好希望結果肯定已初始化。如果不是我們,我們最好希望我們的隊友同樣執行這一點。如果不是現在,我們最好希望未來的開發者也能堅持這一點!

如果我們堅持使用條件三元表達式,就有辦法繞過這個限制。我們只需要將程式碼重構為函數即可。這絕對是說來容易做來難。這個噱頭很快就過時了!

function computeWrappedValue() {
    // ...
    return value;
}

function computeWrappedAnother() {
    // ...
    return another;
}

// How cumbersome!
const result = condition ? computeWrappedValue() : computeWrappedAnother();

基於表達式的程式語言(例如 Rust)有更優雅的解決方案。透過將 if 語句 重新分類為 if 表達式 ,可以對每個分支進行求值,從而傳回稍後可以儲存在變數中的值。

// A conditional ternary operator thus looks like this. Each branch
// returns a value, which is captured by the `result` variable.
// We thus ensure that `result` is always initialized by construction.
let result = if condition { value } else { another };
// If we wanted to do something more complex, we use the same syntax.
let result = if condition {
    do_something();
    // In Rust, the last expression without a semicolon is the value
    // that will be "returned" by the overall `if` expression.
    result
} else {
    do_another_thing();
    another
};

我們可以用類似 C 的語言來模擬這個嗎? 你可能早就預見了我的發展方向,但是可以!

一個令人信服的案例

我們想要的是一種在傳回三元分支內的值之前任意執行語句的方法。好吧,對我們來說幸運的是,這正是逗號運算符的用途。

// Parenthesized for clarity.
const result = condition
    ? (doSomething(), value)       // evaluates to `value`
    : (doAnotherThing(), another); // evaluates to `another`

這個公式的巧妙之處在於,分支表達式在必要時才進行計算。我們有效地模擬基於表達式的程式語言的行為。臨時包裝函數的時代已經一去不復返了!

但可惜,我們只能用這種技術走這麼遠。你可以想像,對於一些夠大的 n,將 n 條語句塞進一行已經需要重構到它自己的函數中。就我個人而言,到 n > 時我已經重新考慮了。 3. 就可讀性而言,任何高於此值的結構都是可疑的。

// Maybe we should reconsider here?
const result = condition
    ? (x++, thing = hello(), doSomething(), value)
    : (++y, thing = world(), doAnotherThing(), another);
// Okay, stop. Definitely turn back now!
const result = condition
    ? (
        x++,
        thing = hello(),
        doSomething(),
        doMore(y),
        doEvenMore(thing),
        value,
    ) : (
        ++y,
        thing = world(),
        doAnotherThing(),
        doMore(y),
        doEvenMore(thing),
        another,
    );
// Unless, of course, you're fine with this. It kinda does
// look like a Rust `if` expression if you squint hard enough.

結論

總而言之,我們看到了逗號運算子的一個令人信服的案例:複雜的條件三元運算。當分支又短又甜時,逗號運算子會大放異彩,但在三個內聯語句之後很快就會過時。到那時,重構程式碼可能會更好。

那你應該使用逗號運算子嗎? 老實說......是的!可讀的程式碼會考慮到下一個讀者,因此只要逗號鏈不會太長,我就會接受甚至鼓勵這種編碼風格。如果我們考慮替代方案(即未初始化的變數和重構的微函數),逗號運算子畢竟沒那麼糟。

在實務中,我已經在自己的​​程式碼庫中加入了這些看起來很有趣的逗號運算子。但公平地說,我很少需要多語句三元條件。但當我這樣做時,我的腰帶上就有了一個很酷的工具,可以簡潔地表達我的意圖。

為此,我對逗號運算符提出了令人信服的理由。

以上是逗號運算子的一個令人信服的案例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn