作者 | Nicholas Yang? ? ? ?
譯者 | 彎月? ? ?責(zé)編 | 屠敏
(資料圖片)
出品 | CSDN(ID:CSDNnews)
假設(shè)你正在編寫前端代碼,可選擇的編程語言有多少種?
我認(rèn)為這些語言可大致分為三大陣營(yíng):直接使用 JavaScript、編譯為 WebAssembly 的語言以及編譯為 JavaScript 的語言。
直接使用 JavaScript 需要的工具最少,但代價(jià)是調(diào)試難度非常大,而且也不方便閱讀。盡管新手可以選擇 JavaScript,但除了對(duì)“極簡(jiǎn)主義”的癡迷之外,我看不到太多好處。
編譯為 WebAssembly 的語言雖然在不斷發(fā)展,但仍然處于起步階段。通常,這類編程語言會(huì)產(chǎn)生很大的二進(jìn)制文件,因?yàn)榇蠖鄶?shù)語言都需要提供額外的運(yùn)行時(shí)?;ゲ僮餍匀匀皇且粋€(gè)白日夢(mèng)。即便兩種語言都能編譯成 WebAssembly,也不意味著二者之間一定可以相互交談。而且這些語言仍然需要趕上數(shù)十年來為 DOM 編寫的各種 JavaScript 庫(kù)。WebAssembly 也沒有類似于 React 或 Svelte 的庫(kù)。不要誤會(huì)我的意思,WebAssembly 有自己的使用場(chǎng)合。如果你想在瀏覽器中運(yùn)行包含大量計(jì)算的原生代碼,WebAssembly 是完美的選擇。但我不推薦日常前端開發(fā)選用 WebAssembly。
還有編譯成 JavaScript 的語言。這類語言實(shí)在不成氣候,我們不能對(duì)這個(gè)問題閉口不談。ClojureScript、Elm、ReScript、Dart 等語言都有很不錯(cuò)的社區(qū),但我不認(rèn)為它們的市場(chǎng)份額一定會(huì)擴(kuò)大。這很可惜,畢竟編譯成 JavaScript 的語言可能是在瀏覽器中獲得良好體驗(yàn)的最有效方式。它們?cè)试S你訪問 JavaScript 所沒有的功能,比如靜態(tài)類型、強(qiáng)類型、不變性、宏等。此外,它們還允許你訪問 JavaScript 以及廣泛的 JavaScript 生態(tài)系統(tǒng), 而且它們不需要打包大型運(yùn)行時(shí)。
有人認(rèn)為有了 WebAssembly,人們可能就不愿意將其他語言編譯成 JavaScript,因?yàn)?Wasm 才是瀏覽器編程時(shí)的“正版”編譯目標(biāo)。但我不同意這種觀點(diǎn)。我們需要更多可以編譯成 JavaScript 的語言。在文本中,我想介紹一下我心目中未來的語言是什么樣子。
TypeScript
說起編譯成 JavaScript 的語言,就不得不提到 TypeScript。TypeScript 是一種很棒的語言,顯著提高了開發(fā)人員的體驗(yàn),增加了類型安全,發(fā)展出了更好的工具,而且替換成本也非常低??紤]到整個(gè) JavaScript 生態(tài)系統(tǒng)的現(xiàn)狀以及與類型檢查 JavaScript 本身的難度,我認(rèn)為 TypeScript 團(tuán)隊(duì)取得了非凡的成就。
然而,一些針對(duì) TypeScript 的批評(píng)也很公正??偨Y(jié)起來主要有兩大癥結(jié):性能和合理性。需要注意的是,TypeScript 團(tuán)隊(duì)并非不知道這兩種批評(píng)意見。然而,這是 TypeScript 團(tuán)隊(duì)在開發(fā)之初做出的明確權(quán)衡。在我看來,當(dāng)時(shí)團(tuán)隊(duì)為了能夠?qū)崿F(xiàn) TypeScript 而選擇這些權(quán)衡是非常明智的。
話雖如此,性能可以說是 TypeScript 最常被提及的問題。TypeScript 本身也是用 TypeScript 實(shí)現(xiàn)的,這個(gè)實(shí)現(xiàn)非常復(fù)雜。目前這個(gè)類型系統(tǒng)實(shí)際上是一種迷你編程語言,這會(huì)導(dǎo)致類型檢查的速度非常緩慢。
第二個(gè)問題是合理性。這個(gè)問題不太有人經(jīng)常提及,但專注于編程語言的開發(fā)人員卻經(jīng)常抱怨。TypeScript 有很多“洞”,如 allowJs 配置選項(xiàng)、any 類型和交集類型,因此這個(gè)類型系統(tǒng)無法確保代碼是類型安全的??梢哉f,TypeScript 編寫的代碼依然可能在運(yùn)行時(shí)出錯(cuò)。除此之外,TypeScript 的類型推斷只能處理最簡(jiǎn)單的情況。很多時(shí)候,你必須明確標(biāo)注類型。
然而,這兩個(gè)問題都是深思熟慮后的權(quán)衡結(jié)果。讓編譯器自己編譯自己是測(cè)試 TypeScript 的最好辦法。開發(fā)人員必須從語言的角度了解 TypeScript。更具體地說,他們必須體驗(yàn)編寫大型 JavaScript 代碼庫(kù)的感受,然后逐步在代碼庫(kù)內(nèi)加入類型。TypeScript 選擇了不實(shí)現(xiàn)嚴(yán)格的合理性,這樣開發(fā)人員就可以在現(xiàn)有的 JavaScript 代碼庫(kù)中逐步采用 TypeScript,這也意味著開發(fā)人員只需要使用一個(gè) any 類型就可以擺脫添加類型時(shí)的挫敗感。
可以說,TypeScript 是第一個(gè)純粹為了改善開發(fā)者體驗(yàn)(而非語義)的語言。它沒有添加任何運(yùn)行時(shí)結(jié)構(gòu),對(duì)性能沒有任何影響。相反,它添加了一個(gè)類型系統(tǒng),更重要的是,它教會(huì)了社區(qū)如何使用類型、構(gòu)建高質(zhì)量工具、建立正確的文化。這本身就是一個(gè)不可思議的壯舉。
下一個(gè)瀏覽器語言
所有這一切都表明,TypeScript 在 10 年前做出了一些對(duì)語言產(chǎn)生巨大影響的權(quán)衡。而現(xiàn)在,隨著時(shí)間的流逝,我認(rèn)為是時(shí)候出現(xiàn)一種新的語言,并做出一系列新的權(quán)衡了。具體來說,我想要一種具有合理性、類型推斷,并且能夠快速編譯的編程語言。
但是,這些選擇帶來的相應(yīng)的權(quán)衡是什么?
首先,為了合理性,這門語言不會(huì)嘗試對(duì)各種 JavaScript 模式進(jìn)行類型檢查,相反它會(huì)成為一門單獨(dú)的語言,使用更簡(jiǎn)單的類型系統(tǒng)編譯成 JavaScript。它會(huì)把現(xiàn)有的 JavaScript 代碼視為與之互操作的外部代碼,對(duì) JavaScript 代碼進(jìn)行運(yùn)行時(shí)類型檢查,而且它還會(huì)用不同的原生語言實(shí)現(xiàn)。
為什么我想要這樣的一種語言?
首先,我喜歡編寫具有合理性且相對(duì)簡(jiǎn)單的類型系統(tǒng)的語言。我想要一種既可以在瀏覽器中運(yùn)行,也可以在現(xiàn)有Web生態(tài)系統(tǒng)中運(yùn)行的語言。編譯為 WebAssembly 的語言常常忽略 Web 生態(tài)系統(tǒng)的其余部分。他們想在瀏覽器中逐個(gè)像素地描繪原生 UI。雖然我認(rèn)為這是一個(gè)很好的目標(biāo),但不是我的目標(biāo)。我想使用這種語言來構(gòu)建普通的日常網(wǎng)站。我不想要一種純函數(shù)式語言,我想要一種具有傳統(tǒng) C 風(fēng)格語法的語言,我想要一種語言來體現(xiàn)我在“工具的工具”方面的想法。
為什么我認(rèn)為現(xiàn)在這個(gè)時(shí)間點(diǎn)很合適?答案很明顯,現(xiàn)在是開始學(xué)習(xí)一門語言的第二佳時(shí)間,第一佳時(shí)間是 10 年前。但我也認(rèn)為 JavaScript 社區(qū)在過去十年中發(fā)生了很大變化。人們學(xué)習(xí)了 TypeScript,并習(xí)慣了接收編譯器的反饋,也習(xí)慣了對(duì)數(shù)據(jù)進(jìn)行建模。人們開始使用 Rust、Swift 和 Kotlin 等語言。人們開始懂得優(yōu)秀的工具的意義。這并不是說十年前人們會(huì)拒絕類型安全的語言,只不過當(dāng)時(shí)很難廣泛采用。
ReScript/ReasonML
有些人可能覺得我描述的這種語言聽起來非常像 ReScript/ReasonML。
沒錯(cuò),二者在某些方面確實(shí)有重疊。但是,我的語言在理想情況下應(yīng)該對(duì) JavaScript 代碼和特征進(jìn)行運(yùn)行時(shí)類型檢查。運(yùn)行時(shí)類型檢查有助于實(shí)現(xiàn)良好的互操作。使用其他 JavaScript 庫(kù)會(huì)相對(duì)容易一些。而且,我相信 traits 更適合用戶,它們可以映射成其他語言的特性,如 Java 接口和 C++ 概念。利用 traits 可以輕易實(shí)現(xiàn)一些特性,比如通過 Display trait 實(shí)現(xiàn)輸出任何類型。這個(gè)功能雖然看起來很淺顯,但可以避免一些本不應(yīng)該出現(xiàn)的易用性方面的奇怪問題,例如“如何輸出這個(gè)?”或者“為什么整數(shù)加法需要使用+,而浮點(diǎn)小數(shù)的加法需要使用+.”等。此外,我希望刪除一些額外的東西,如對(duì)象、鏈表、多態(tài)變體等。根據(jù)上一次的嘗試,我也不喜歡 ReScript 的開發(fā)人員體驗(yàn)和錯(cuò)誤消息。
也就是說,我不排除 ReScript 有可能成為正確選項(xiàng)的可能性。由于我的經(jīng)驗(yàn)來自多年前,所以我認(rèn)為可以再給這門語言一次機(jī)會(huì)。
類型安全
我希望我的語言能用一種更系統(tǒng)的方法來實(shí)現(xiàn)類型安全。具體來說,我想采用 Rust 中實(shí)現(xiàn)不安全塊的方式來實(shí)現(xiàn)與 JavaScript 之間的互操作。也就是說,如果想調(diào)用 JavaScript,需要將代碼包裝在一個(gè)不安全的塊中。這可以作為一個(gè)明確的標(biāo)志,表明你需要更仔細(xì)地閱讀這段代碼。接下來只需為這些不安全的 JavaScript 庫(kù)調(diào)用編寫綁定即可。剛開始的時(shí)候,這個(gè)過程可以是手動(dòng)的,但希望將來出現(xiàn)類似于 bindgen 和 cxx 之類的功能。
在 JavaScript 中,使用不安全塊的概念似乎是一個(gè)奇怪的選擇。JavaScript 的不安全和C的不安全并不一樣。但很多人沒有意識(shí)到的是,安全性指的并不僅僅是網(wǎng)絡(luò)安全。
安全指的是能夠放心使用一個(gè)值、無需擔(dān)心它可能為 null 的能力。安全指的是能夠利用類型對(duì)領(lǐng)域進(jìn)行建模,并且能確信建模是正確的能力。安全指的是可以隨意改變其內(nèi)容,而無需擔(dān)心引入錯(cuò)誤或混亂的能力。JavaScript 由于其動(dòng)態(tài)的特性,本質(zhì)上就是不安全的。而 Rust 的不安全塊可以讓用戶在擁有安全區(qū)的前提下,訪問大規(guī)模的不安全代碼?;跒g覽器的語言也應(yīng)當(dāng)如此。
至于運(yùn)行時(shí)檢查,我相信為這個(gè)功能付出的額外開銷是值得的。我們已經(jīng)有許多在 JavaScript 中進(jìn)行模式驗(yàn)證的先例了,只不過還沒有通用的規(guī)則。現(xiàn)在的模式驗(yàn)證一般是自動(dòng)推斷出一種能夠在運(yùn)行時(shí)出錯(cuò)的語言類型,或者對(duì) JavaScript 值進(jìn)行模式匹配。
WebAssembly
我對(duì) WebAssembly 未來的發(fā)展依然持樂觀態(tài)度。雖然新功能從建議到實(shí)現(xiàn)需要很長(zhǎng)時(shí)間,但這可能是為了保證其質(zhì)量。但我認(rèn)為 WebAssembly 不一定會(huì)成為瀏覽器的通用運(yùn)行時(shí)??赡苓@一點(diǎn)會(huì)有改變,但我認(rèn)為 WebAssembly 更像是一種硬件加速器。當(dāng)用戶需要使用適合硬件特性的強(qiáng)大計(jì)算時(shí)(比如固定寬度整數(shù)、靜態(tài)函數(shù)調(diào)用等),就會(huì)使用 WebAssembly ,就像是想用并行計(jì)算的用戶會(huì)使用GPU一樣。我認(rèn)為,這種模型有實(shí)現(xiàn)異構(gòu)計(jì)算的潛力,比如部分代碼編譯成 JavaScript、部分代碼編譯成 WebAssembly。這一點(diǎn)可以由用戶主動(dòng)控制,也可以自動(dòng)實(shí)現(xiàn),甚至可以通過即時(shí)編譯實(shí)現(xiàn)。也許,通過對(duì) JavaScript和Wasm 代碼的控制,編譯器可以最小化兩者之間的交互,從而提高性能。甚至可以做成類似于將代碼發(fā)給 WebGPU 之類的機(jī)制。
有可能有了這種模型,編寫需要大量計(jì)算的程序(如機(jī)器學(xué)習(xí)模型、視頻游戲、渲染軟件)就更容易了。
這種將代碼編譯成 WebAssembly 和 JavaScript 的概念也可以體現(xiàn)在語言中。我希望能明確指定整型和浮點(diǎn)型,最好 Rust 的 usize 之類的索引類型。這樣,如果代碼被編譯成 WebAssembly,就能享受 WebAssembly 的固定寬度整數(shù)帶來的好處。另一種可能性是,可以建立語言的一個(gè)子集,該子集可以編譯成 Wasm,并限制一些動(dòng)態(tài)特性,如閉包、垃圾回收等。要訪問該子集,需要使用另一種 unsafe 風(fēng)格的塊(或許可以是 strict 塊),或者也可以讓子集通過 dynamic塊訪問外部代碼。這些都是假設(shè),但我認(rèn)為值得一試。
實(shí)現(xiàn)
該語言很可能用 Rust 實(shí)現(xiàn)。主要是因?yàn)槲液芟矚g Rust,我相信 Rust 帶來的算術(shù)數(shù)據(jù)類型、相對(duì)較快的代碼、有限但夠用的可修改性和豐富的庫(kù)非常適合編寫編譯器。
如果 WebAssembly 進(jìn)化得足夠好,其性能接近原生代碼,我就會(huì)考慮用該語言的一個(gè)子集來編譯這個(gè)編譯器本身,將其編譯為快速的 WebAssembly。但至少 Rust 編寫的編譯器應(yīng)該夠用幾年了。
結(jié)論
你也許注意到了,類型安全和 WebAssembly 這兩節(jié)實(shí)際上是在討論系統(tǒng)語言的思想,如不安全塊、硬件加速等,想辦法將它們應(yīng)用到基于瀏覽器的語言上。這種語言的設(shè)計(jì)就是這樣的。一些很有趣的編程語言都是在系統(tǒng)層次上實(shí)現(xiàn)的。我希望能在瀏覽器中實(shí)現(xiàn)它們。
雖然本文標(biāo)題是《下一代瀏覽器語言》,但我想澄清一點(diǎn),這并不是單一的一門語言。我希望出現(xiàn)多種語言,嘗試多種思路。希望本文能拋磚引玉,激發(fā)讀者在瀏覽器語言領(lǐng)域創(chuàng)新的興趣。
關(guān)鍵詞: JavaScript 未必是最優(yōu)選 下一代瀏覽器語言會(huì)是什么樣 javascript