JS語(yǔ)法也可以有C#的switch表達(dá)式
正文
于 C/Java 語(yǔ)系的語(yǔ)言,都有 switch 語(yǔ)法。switch 語(yǔ)法用于多分支是一個(gè)標(biāo)準(zhǔn)的用法,但這個(gè)分支語(yǔ)法的各分支之間存在穿透性,所以需要 break
來(lái)切斷邏輯,這也成為 switch 語(yǔ)法中最重要的一個(gè)替在缺陷來(lái)源。此外,由于 switch 語(yǔ)句中各 case 的代碼是在同一個(gè)作用域中,也會(huì)對(duì)代碼造成一些不便。
C# 8.0 引入了 switch 表達(dá)式。C# 的 switch 表達(dá)式有著非常豐富的語(yǔ)法元素,可以和模式匹配和解構(gòu)等語(yǔ)法元素協(xié)同工作 —— 這些都不在這里細(xì)說(shuō),但是對(duì)傳統(tǒng)的 switch 語(yǔ)句 進(jìn)行了一些改進(jìn):
- 通過(guò)箭頭 (
=>
) 標(biāo)記處理了 case 和語(yǔ)句之間的一對(duì)一關(guān)系,不需要 break,不再穿透; - 作為表達(dá)式,可以而且必須返回值;
新的約束
- switch 表達(dá)式一定要詳盡(邏輯一定會(huì)走進(jìn)某一個(gè) case,可以通過(guò)棄元模式兜底),否則可能會(huì)在運(yùn)行時(shí)引發(fā)異常。
在 C# 8.0 發(fā)布的同年,Java 12 也發(fā)布并引入了 switch 表達(dá)式預(yù)覽。Java 的 switch 表達(dá)式實(shí)現(xiàn)比較簡(jiǎn)單,就是 switch 語(yǔ)句到 switch 表達(dá)式的直接轉(zhuǎn)換,僅支持等值匹配。直到 2023 年 3 月 Java 20 發(fā)布,switch 表達(dá)式才開(kāi)始支持模式匹配。相比之下,Kotlin 的 when 表達(dá)式走在了前面。
在這個(gè)問(wèn)題上 JavaScript 似乎走在了后面,不過(guò)在語(yǔ)言提供 switch 表達(dá)式之前,我們可以嘗試自己造個(gè)輪子。
思路當(dāng)然是參考策略模式。假設(shè)有一個(gè)列表,這個(gè)列表里的每個(gè)元素都包含了兩個(gè)因素:第一個(gè)用于判斷是否命中,第二個(gè)是個(gè)函數(shù),得到一個(gè)計(jì)算結(jié)果。然后寫一個(gè)循環(huán)遍歷列表的每個(gè)元素,一旦某個(gè)元素命中,就執(zhí)行元素?cái)y帶的函數(shù)獲得結(jié)果,中斷循環(huán),返回結(jié)果。如果列表的最后一個(gè)元素必定命中,那么這個(gè)列表就是“詳盡”的。
when 函數(shù)寫法
那么這個(gè) when
函數(shù)可能會(huì)這樣寫(switch
是關(guān)鍵字,所以使用 when
來(lái)作為函數(shù)名):
// JS function when(value, ...cases) { for (const { is, run } of cases) { if (is(value)) { return run(value); } } throw new Error("非詳盡"); }
這里我們假設(shè)每個(gè)情況 (case) 都含有 is
方法用于判斷是否命中,用 run
方法保存命中后需要執(zhí)行的操作。
分算等級(jí)”測(cè)試
相應(yīng)地,我們可以經(jīng)典的“拿分算等級(jí)”來(lái)進(jìn)行測(cè)試:
// JS function calcGrade(score) { return when( score, { is: v => v >= 0 && v < 80, run: v => `不合格 (${v})` }, { is: v => v >= 80 && v < 100, run: v => `合格 (${v})` }, { is: v => v == 100, run: v => `滿分 (${v})` }, { is: _ => true, run: v => `無(wú)效 (${v})` }, ); } for (let i = 0; i < 50; i++) { const v = 70 + ~~(Math.random() * 35); console.log(calcGrade(v)); }
在 calcGrade
實(shí)現(xiàn)中 when
的 case 列表最后一項(xiàng)采用了“永真”斷言,所以走到這一項(xiàng)的時(shí)候一定會(huì)命中,從邏輯上來(lái)永遠(yuǎn)不會(huì)觸發(fā) Error。如果是非“詳盡”的情況列表,就有可能觸發(fā) Error。
測(cè)試問(wèn)題
不過(guò)現(xiàn)在從測(cè)試代碼中就發(fā)現(xiàn)了兩個(gè)問(wèn)題:
is
斷言是采用函數(shù)的形式,不能簡(jiǎn)單地直接按值匹配;- 從調(diào)用形式上來(lái)說(shuō),
score
和后面的 case 元素是同級(jí)的,形式上區(qū)分不明顯; - 每次都要寫
is
和run
,條件多了寫起來(lái)也煩。
繼續(xù)改進(jìn) ——
// JS function when(value) { // when 的參數(shù)先給 switch 的值 // 返回一個(gè)函數(shù)來(lái)處理分支匹配 ② return function (...cases) { for (const [is, run] of cases) { // ^^^^^^^^^ 從對(duì)象改為元組(數(shù)組)③ if (value === is || (typeof is == "function" && is(value))) { // ^^^^^^ 精確判斷 ① // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 斷言函數(shù)判斷 return run(value); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 可指定行為(函數(shù)) } } throw new Error("非詳盡"); }; } function calcGrade(score) { return when(score)( // ^^^^^^^^^^^ 這里返回的是匹配處理的函數(shù) [v => v >= 0 && v < 80, v => `不合格 (${v})`], [v => v >= 80 && v < 100, v => `合格 (${v})`], [100, () => "滿分 (100)"], // ^^^ 可以指定匹配的值 // ^^ 計(jì)算不需要參數(shù),可以不聲明 [_ => true, v => `無(wú)效 (${v})`], // ^^^^^^^^^ 兜底的永真斷言 ); }
為什么兜底斷言必須使用一個(gè)函數(shù)呢?因?yàn)?nbsp;true
值也有可能是對(duì)應(yīng)一種預(yù)想的分支情況。由于這個(gè) when
是通過(guò)語(yǔ)義來(lái)實(shí)現(xiàn)而不是通過(guò)語(yǔ)法來(lái)實(shí)現(xiàn)的,所以這里沒(méi)辦法定義一個(gè)安全的兜底斷言語(yǔ)法,只有用斷言函數(shù)會(huì)相對(duì)安全。
升級(jí)成 TypeScript
至此為止我們已經(jīng)基本實(shí)現(xiàn)了 switch 表達(dá)式 (when),把它升級(jí)成 TypeScript
// TypeScript type CaseCondition<T> = T extends Function ? never : ((t: T) => boolean) | T type Case<T, R> = [CaseCondition<T>, (t: T) => R]; function when<T>(value: T): <R>(...cases: Case<T, R>[]) => R { return function<R>(...cases: Case<T, R>[]): R { for (const [is, run] of cases) { if (value === is || (typeof is == "function" && is(value))) { return run(value); } } throw new Error("非詳盡"); }; } function calcGrade(score: number) { return when(score)( [v => v >= 0 && v < 80, v => `不合格 (${v})`], [v => v >= 80 && v < 100, v => `合格 (${v})`], [100, () => "滿分 (100)"], [_ => true, v => `無(wú)效 (${v})`], ); }
這段代碼當(dāng)然可以直接用,但是如果使用 npm 可能會(huì)更方便一點(diǎn):
npm install @jamesfancy/when
// TypeScript import { when } from "@jamesfancy/when"; function calcGrade(score: number) { return when(score)( [v => v >= 0 && v < 80, v => `不合格 (${v})`], [v => v >= 80 && v < 100, v => `合格 (${v})`], [100, () => "滿分 (100)"], [_ => true, v => `無(wú)效 (${v})`], ); }
以上就是JS語(yǔ)法也可以有C# 的switch表達(dá)式的詳細(xì)內(nèi)容,更多關(guān)于JS語(yǔ)法C# switch表達(dá)式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
父節(jié)點(diǎn)獲取子節(jié)點(diǎn)的字符串示例代碼
這篇文章主要介紹了父節(jié)點(diǎn)獲取子節(jié)點(diǎn)的字符串的方法,需要的朋友可以參考下2014-02-02ECharts事件處理與旭日?qǐng)D實(shí)現(xiàn)
這篇文章介紹了ECharts事件處理與實(shí)現(xiàn)旭日?qǐng)D的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06完美實(shí)現(xiàn)js選項(xiàng)卡切換效果(二)
這篇文章主要為大家詳細(xì)介紹如何完美實(shí)現(xiàn)js選項(xiàng)卡切換效果,通過(guò)設(shè)置定時(shí)器實(shí)現(xiàn)延時(shí)0.5s切換選項(xiàng)卡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03JS如何實(shí)現(xiàn)網(wǎng)站中PC端和手機(jī)端自動(dòng)識(shí)別并跳轉(zhuǎn)對(duì)應(yīng)的代碼
這篇文章主要介紹了JS如何實(shí)現(xiàn)網(wǎng)站中PC端和手機(jī)端自動(dòng)識(shí)別并跳轉(zhuǎn)對(duì)應(yīng)的代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01Javascript string 擴(kuò)展庫(kù)代碼
Javascript原生的String處理函數(shù)顯得很不夠豐富2010-04-04用javascript實(shí)現(xiàn)的電信鐵通(網(wǎng)通)自動(dòng)跳轉(zhuǎn)源代碼
用javascript實(shí)現(xiàn)的電信鐵通(網(wǎng)通)自動(dòng)跳轉(zhuǎn)源代碼...2007-11-11關(guān)于var在for循環(huán)遇到的問(wèn)題解決
這篇文章主要給大家介紹了關(guān)于var在for循環(huán)遇到的問(wèn)題的幾種解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-07-07第二次聊一聊JS require.js模塊化工具的基礎(chǔ)知識(shí)
第二次聊一聊JS require.js模塊化工具的基礎(chǔ)知識(shí),本文為大家JS require.js模塊化工具的最基本知識(shí)點(diǎn),感興趣的小伙伴們可以參考一下2016-04-04js之input[type=file]選擇重復(fù)的文件,無(wú)法觸發(fā)change事件問(wèn)題
這篇文章主要介紹了js之input[type=file]選擇重復(fù)的文件,無(wú)法觸發(fā)change事件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05