JavaScript的進化和未來

威靈頓發表於2019-12-10

這個話題應該由設計開發JavaScript的人來寫,因為話題範圍比較大,不過我不是。不過作為熟悉多種程式語言的開發者,我仍然覺得有不少話要說,就挑選我想過的部分講好了。

enter image description here

歷史:

關於JavaScript的歷史,想必大家早就清楚了,這個語言先天設計不良,但是又充滿活力,運氣又很好。參考:為什麼是Javascript

進化:

JavaScript語言進化很快,這跟它的標準改進流程直接相關,後面會說到。

對比:

JavaScript語言一直在吸收各個語言社群創造的良好實踐。它自己也有一個非常龐大、有活力的社群。它將是未來很多應用開發語言選型的有力競爭者。如果再加上TypeScript的加強,更是不可小覷。

JavaScript ES6:

我分享了一個腦圖,在這裡:JavaScript ES6

這個腦圖,對JavaScript ES6做了系統的總結和介紹,可以當一個索引。 相對於過去的Js語言標準,ES6有著巨大優勢。全面修復了過去JavaScript語言的各種設計缺陷,對已有的功能又做了進一步的加強。 總的來說,ES6吸收了其它語言的精華 - 特別是Python,以及社群的成功實踐經驗,對Js語言標準是一個重要升級,後續的升級反倒更像小修小補。

首先它試圖讓語言更為嚴謹,語言的元件“正交化”、”概念統一化“。我們可以看看Unix/Linux,設計裡面有一個重要 的特色,就是:“一切皆檔案”。通過操縱file介面,幾乎可以操縱一切物件,包括核心/檔案。這個思想為軟體設計帶來了重要的優點 - 統一的操作模式,帶來靈活的設計方式。

而JavaScript ES6裡面,大量引入迭代器概念,把迭代器介面的支援,擴充套件到了很多語言要素上。比如,字串/map/set/陣列等等。 這意味著,當語言執行的時候,操縱的資料物件可以是抽象的,是隨時替換的,非常的靈活,它給出了操作物件資料的 一致模式,編寫程式碼也更為便利,重用容易。

語言的缺陷修復

原始的JavaScript設計缺陷非常的多。這個連作者自己都承認 - “原創的並不優秀,優秀的並非原創。” 歸根結底,JavaScript設計的時間太短,欠考慮的地方實在太多,缺乏社群的實踐和打磨。《JavaScript語言精粹》就特別說明了 JavaScript語言的優點和糟粕。

所以ES6裡面,幾乎把原有的糟粕都修復了,當然為了向下相容,原有的設計仍然保留了下來,這帶來了隱患,也帶來了更多的語言複雜性。因為為了修復,只好用新的語法,同時保留舊的關鍵字和設計,維持舊程式碼的相容性。

比如var的變數定義,有作用域內的提升問題,但是let宣告就沒有。最讓人頭大、捉摸不定的this,這下徹底的釘死了 - 只要你使用了箭頭函式。

全域性變數的頂層關聯問題,也做了修正。原來的js連模組系統都沒有設計,導致社群出現各種所謂的”方案“,奇淫技巧用個遍。 這次定義了module的import/export操作規範,算是徹底解決了這個缺陷。

特色功能

生成器和async/await

這次ES6跟Python“抄襲”了很多設計,最重要的便是:“Generator” - 生成器。不過在Python裡面,一般生成器只當迭代 介面用,作為”惰性“獲取資料的一個模式,只是它同時具備”中途返回“的執行模式和程式碼排程的能力。但是恰恰因為它具備程式碼排程能力,在JavaScript中,這個功能得到了充分挖掘。

過去,Node技術棧開發,最容易出現錯誤的地方就是處理過程的同步操作,就是程式碼的執行順序問題。因為Node的Api,幾乎都是非同步回撥介面,想知道結果, 必須在回撥函式裡面等待。比如一個功能需要按1-2-3-4的步驟處理,這個在非非同步的語言中就不是問題,是天然的動作,從上到下處理。 但是Node不同,要麼寫在回撥中層層巢狀 - 弊端較大,要麼使用Promise模式,改造成連綴式then呼叫 - 程式碼的侵入性較強,或者使用 第三方lib,強制處理過程註冊到規定的資料結構,統一順序處理。這些模式,都有各自的弊端。歸根結底,非同步不符合人類的思維習慣。

如果處理邏輯牽扯比較多的順序控制,比如處理鏈條是:1-2-3-4-5-6-7-8等等,這個就會帶來很大的開發困難,程式碼易錯,難以除錯和維護。 難怪以前有人認為,Node不適合這種需要複雜邏輯處理的應用場景。參考:Node

但是,ES6版本的JavaScript支援了生成器,以及包裝生成器的語法糖:async和await,近乎完美(至少目前看是)的解決了這個程式碼的執行順序問題。因為,通過生成器的程式碼排程行為,可以讓程式碼非同步執行,卻按同步書寫,這符合人的思維習慣。

不過把async/await全歸到“生成器”,也不準確,它更像是一個組合模式,同時組合了生成器的執行排程和Promise的非同步執行,因為await返回的 都是Promies物件。想下Ajax,Ajax便是多種要素的組合,形成了一套操作模式。

比如:

const asyncProcessFile = async function () {
const file1 = await readFile('/demo/file1');
const file2 = await readFile('/demo/file2');
const file3 = await readFile('/demo/file3');
processFiles(file1, file2, file3);
}

以上程式碼,await保證了執行的順序,告訴使用者,要等後面的結果出來,最後一起處理檔案的內容。 所以,選用Node技術棧的框架,也應該優先使用ES6版本Js的產品。

解構賦值

解構賦值,其實是根據目標物件的結構,通過適當的簡化語法,一次性的解析/讀取物件的屬性值或內容,這樣就不必像其它語言那樣,層層遞進的 解析結構,再逐個獲得結果,賦予變數。

  • 最簡單的是陣列: let [a, b, c] = [1, 2, 3];

a, b, c 自動獲得對應1, 2, 3的數值

  • 物件也可以支援:

let { foo, bar } = { foo: 'a', bar: 'b' };
foo // 'a' bar // 'b'

深入支援

ES6版本的Js,增加了一些新的概念和元件,增強了語言的內部功能。比如Proxy,Reflect,Symbol等,不過一些功能應該主要是為工具開發者/類庫 開發者提供的,普通使用者使用的機率不高。

未來擴充套件

需要注意的是:ES6雖然在2015年就作為標準推出,但是實現總是需要一個過程的。目前實現主要有兩個地方:瀏覽器和Node。Node對ES6的支援比瀏覽器 要更好。所以開發的時候,儘量選更好的執行環境。

在ES6之後,還有一些提案推出,會進入後續的JavaScript標準中。JavaScript的提案門檻不高,社群內的任何人都可以提交提案 - 很民主,可以請求修改語言標準。不過, 正因為如此,JavaScript的語言特性增加的比較快,相比之下,Python要剋制的多,它的原則是,如果類庫可以很好的支援,就不要修改語言,增加特性。

目前增加的有關語言特性的提案,目前基本都是對語言已有功能的增強,大幅修改或者全新功能比較少。目前可能是傾向於先吸收其它語言較好的、適合JavaScript的 特性,這在目前還好。但是,不排除因為語言功能流行風潮的出現,來一波新功能大幅增加的操作。但是流行的未必是靠譜的,未必是真正合適、合理的。

因為增加特性支援功能有隱患,就是語言的特性會產生互動作用,結果沒準出乎意料,造成意想不到的後果。

而且,語言的特性越多,學習使用就越複雜,給使用者帶來更多的壓力和困擾 - 想想C++。更何況,JavaScript為了相容性,老的錯誤設計一樣也沒廢除,照舊支援, 這個相當於多出來一部分“外掛”,麻煩也不小。如此發展下去,JavaScript開發可能變成一項具有相當難度的工作,沒準普通水平的JavaScript開發者,前端工作都難以勝任。

你們看看現在JavaScript有多少種迴圈模式?

所以,一個建議就是,儘量使用語言的優良特性,使用它的優良子集,使用設計出來的優秀部分編寫簡單可靠的程式,而不是基於壞的設計書寫“巧妙”的程式碼。

作者部落格

相關文章