爲何用 JavaScript 學習函數式編程?(軟件編寫)(第二部分)

煙霧的方塊藝術 —MattysFlicks —(CC BY 2.0)javascript

注意:這是從基礎學習函數式編程和使用 JavaScript ES6+ 撰寫軟件的第二部分。保持關注,接下來還有不少!
第一篇 | 第三篇 >html

忘掉你認爲知道的關於 JavaScript 的一切,用初學者的眼光去看待它。爲了幫助你作到這一點,咱們將會從頭複習一下 JavaScript 的基礎,就像你與其還沒有謀面同樣。若是你是初學者,那你就很幸運了。最終從零開始探索 ES6 和函數式編程!但願全部的概念都被解釋清楚 — 但不要太依賴於此。前端

若是你是已經熟悉 JavaScript 或者純函數式語言的老開發者了,也許你會認爲 JavaScript 是探索函數式編程有趣的選擇。把這些想法放在一邊,用更開放的思想接觸它,你會發現 JavaScript 編程更高層次的東西。一些你歷來不知道的東西。java

因爲這個被稱爲「組合式軟件」,同時函數式編程是明顯的構建軟件的方法(使用函數組合,高階函數等等),你也許想知道爲何我不用 Haskell、ClojureScript,或者 Elm,而是 JavaScript。node

JavaScript 有函數式編程所須要的最重要的特性:react

  1. 一級公民函數: 使用函數做爲數據值的能力:用函數傳參,返回函數,用函數作變量和對象屬性。這個屬性容許更高級別的函數,使偏函數應用、柯里化和組合成爲可能。
  2. 匿名函數和簡潔的 lambda 語法: x => x * 2 是 JavaScript 中有效的函數表達式。簡潔的 lambda 語法使得高階函數變的簡單。
  3. 閉包: 閉包是一個有着本身獨立做用域的捆綁函數。閉包在函數被建立時被建立。當一個函數在另外一個函數內部被建立,它能夠訪問外部函數的變量,即便在外部函數退出後。經過閉包偏函數應用能夠獲取內部固定參數。固定的參數時綁定在返回函數的做用域範圍內的參數。在 add2(1)(2) 中,1add2(1) 返回的函數中的固定參數。

JavaScript 缺乏了什麼

JavaScript 是多範式語言,意味着它支持多種風格的編程。其餘被 JavaScript 支持的風格包括過程式(命令式)編程(好比 C),把函數看做能夠被重複調用和組織的子程序指令;面向對象編程,對象— 而不是函數— 做爲初始構造塊;固然,還有函數式編程。多範式編程語言的劣性在於命令式和麪向對象每每意味着全部東西都是可變的。android

可變性指的是數據結構上的變化。好比:ios

const foo = {
  bar: 'baz'
};

foo.bar = 'qux'; // 改變複製代碼

對象一般須要可變性以便於被方法更新值,在命令式的語言中,大部分的數據結構可變以便於數組和對象的高效操做。git

下面是一些函數式語言擁有可是 JavaScript 沒有的特性:github

  1. 純粹性: 在一些函數式語言中,純粹性是強制的,有反作用的表達式是不被容許的。
  2. 不可變性: 一些函數式語言不容許轉變,採用表達式來產生新的數據結構來代替更改一個已存的數據結構,好比說數組或者對象。這樣看起來可能不夠高效,可是大多數函數式語言在引擎下使用 trie 數據結構,具備結構共享的特色:意味着舊的對象和新的對象是對相同數據的引用。
  3. 遞歸: 遞歸是函數引用自身來進行迭代的能力。在大多數函數式語言中,遞歸是迭代的惟一方式,它們沒有像 forwhiledo 這類循環語句。

純粹性: 在 JavaScript 中,純粹性由約定來達成,若是你不是使用純函數來構成你的大多數應用,那麼你就不是在進行函數式風格的編程。很不幸,在 JavaScript 中,你很容易就會不當心建立和使用一些不純的函數。

不可變性: 在純函數式語言中,不可變性一般是強制的,JavaScript 缺乏函數式語言中高效的、基於 trie 樹的數據結構,可是你可使用一些庫,包括 Immutable.jsMori,由衷指望將來的 ECMAScript 規範版本能夠擁抱不可變數據結構。

有一些跡象帶來了但願,好比說在 ES6 中添加了 const 關鍵字,const 聲明的變量不能被從新賦值,重要的是要理解 const 所聲明的值並非不可改變的。

const 聲明的對象不能被從新聲明爲新的對象,可是對象的屬性倒是可變的,JavaScript 有 freeze() 對象的能力,可是這些對象只能在根實例上被凍結,意味着嵌套着的對象仍是能夠改變它的屬性。換句話說,在 JavaScript 規範中看到真正的不可變還有很長的路要走。

遞歸: JavaScript 技術上支持遞歸,可是大多數函數式語言都有尾部調用優化的特性,尾部調用優化是一個容許遞歸的函數重用堆棧幀來遞歸調用的特性。

沒有尾部調用優化,一個調用的棧極可能沒有邊界致使堆棧溢出。JavaScript 在 ES6 規範中有一個有限的尾調用優化。不幸的是,只有一個主要的瀏覽器引擎支持它,這個優化被部分應用隨後從 Babel(最流行的 JavaScript 編譯器,在舊的瀏覽器中被用來把 ES6 編譯到 ES5) 中移除。

最重要的事實:如今使用遞歸來做爲大的迭代還不是很安全 — 即便你很當心的調用尾部的函數。

什麼又是 JavaScript 擁有可是純函數式語言缺少的

一個純粹主義者會告訴你 JavaScript 的可變性是它的重大缺點,這是事實。可是,引發的反作用和改變有時候頗有用。事實上,不可能在規避全部反作用的狀況下開發有用的現代應用。純函數式語言好比說 Haskell 使用反作用,使用 monads 包將有反作用的函數假裝成純函數,從而使程序保持純淨,儘管用 Monads 所帶來的反作用是不純淨的。

Monads 的問題是,儘管它的使用很簡單,可是對一個不是很熟悉它的人解釋清楚它有點像「對牛談琴」。

「Monad說白了不過就是自函子範疇上的一個幺半羣而已,這有什麼難以理解的?」 ~James Iry 所引用 Philip Wadler 的話,解釋一個 Saunders Mac Lane 說過的名言。「編程語言簡要、不完整之黑歷史」

典型的,這是在調侃這有趣的一點。在上面的引用中,關於 Monads 的解釋相比最初的有了很大的簡化,原來是下面這樣:

X 中的 monad 是其 endofunctor 範疇的幺半羣,生成 endofunctor 和被 endofunctor 單位 set 組合所代替的 X 」 ~ Saunders Mac Lane。 "Categories for the Working Mathematician"

儘管這樣,在個人觀點看來,懼怕 Monads 是沒有必要的,學習 Monads 最好的方法不是去讀關於它的一堆書和博客,而是馬上去使用它。對於大部分的函數式編程語言來講,晦澀的學術詞彙比它實際概念難的多,相信我,你沒必要經過了解 Saunders Mac Lane 來了解函數式編程。

儘管它不是對全部的編程風格都絕對完美,JavaScript 無疑是做爲適應各類編程風格和背景的人的通用編程語言被設計出來的。

根據 Brendan Eric 所言,在一開始的時候,網景公司就有意適應兩類開發者:

「...寫組件的,好比說 C++ 或者 Java;寫腳本的、業餘的和愛好者,好比直接寫嵌在 HTML 裏的代碼的。」

原本,網景公司的意向是支持兩種不一樣的語言,同時腳本語言大體要像 Scheme (一個 Lisp 的方言),並且,Brendan Eich:

「我被招聘到網景公司,目的是在瀏覽器中 作一些 Scheme」。

JavaScript 應當是一門新的語言:

「上級工程管理的命令是這門語言應當像 Java,這就排除了 Perl,Python,和 Tcl,以及 Scheme。」

因此,Brendan Eich 最初腦子裏的想法是:

  1. 瀏覽器中的 Scheme。
  2. 看起來像 Java。

它最終更像是個大雜燴:

「我不驕傲,但我很高興我選擇了 Scheme 的一類函數和 Self(儘管奇怪)的原型做爲主要的元素。」因爲 Java 的影響,特別是 y2k 的 Date 問題以及對象的區別(好比 string 和 String),就不幸了。」

我列出了這些 「很差的」 的類 Java 特性,最後整理成 JavaScript:

  • 構造函數和 new 關鍵子,跟工廠函數有着不一樣的調用和使用語義。
  • class 的關鍵字和單一父類 extends 做爲最初的繼承機制。
  • 用戶更習慣於把 class 看做是它的靜態類型(實際並不是如此)。

個人意見:永遠避免使用這些東西。

很幸運 JavaScript 成爲了這樣厲害的語言,由於事實上證實腳本的方式贏了那些創建在「組件」上的方式(如今,Java、Flash、和 ActiveX 擴展已經不被大部分安裝的瀏覽器支持)。

咱們最終創做了一個直接被瀏覽器支持的語言:JavaScript。

那意味着瀏覽器能夠減小臃腫和問題,由於它們如今只須要支持一種語言:JavaScript。你也許認爲 WebAssembly 是例外,可是 WebAssembly 設計之初的目的是使用兼容的抽象語法樹來共享 JavaScript 的語言綁定(AST)。事實上,最先的把 WebAssembly 編譯成 JavaScript 的子集的示範是 ASM.js。

做爲 web 平臺惟一的通用標準編程語言,JavaScript 在軟件歷史潮流中乘風直上:

App 吞食世界, web 吞食 app, 同時 JavaScript 吞食 web。

根據多個平臺調查JavaScript 是目前世界上最流行的語言。

JavaScript 並非函數式編程的理想化工具,可是它倒是爲大型的分佈式的團隊開發大型應用的好工具,由於不一樣的團隊對於如何構建一個應用或許有不一樣的見解。

一些團隊致力於腳本化,那麼命令式的編程就特別有用,另一些更精於抽象架構,那麼一點保留的面向對象方法也許不失爲壞。還有一些擁抱函數式編程,使用純函數來確保穩定性、可測試性和項目狀態管理以便減小用戶的反饋。團隊裏的這些人可使用相同的語言,意味着他們能夠更好的交換想法,互相學習和在其餘人的基礎上更進一步的開發。

在 JavaScript 中,全部這些想法能夠共存,這樣就讓更多的人開始擁抱 JavaScript,而後就產生了世界上最大的開源包管理器 (2017 年 2 月),npm

JavaScript 的真正優點在於其生態系統中的思想和用戶的多樣性。它也許不是純函數式編程最理想的語言,但它是你能夠想象的工做在不一樣平臺的人共同合做的理想語言,好比說 Java、Lisp 或者 C。JavaScript 也許並不對有這些背景的用戶徹底友好,可是這些人很樂意學習這門語言並迅速投入生產。

我贊成 JavaScript 並非對函數式編程者最好的語言。可是,沒有任何其餘語言能夠聲稱他們能夠被全部人使用,同時正如 ES6 所述:JavaScript 能夠知足到更與喜歡函數式編程的人的須要,同時也愈來愈好。相比於拋棄 JavaScript 和世界上幾乎每家公司都使用的使人難以置信的生態系統,爲何不擁抱它,把它變成一個更適合軟件組合化的語言?

如今,JavaScript 已是一門足夠優秀的函數式編程語言,意味着人們可使用 JavaScript 的函數式編程方法來構造不少有趣的和有用的東西。Netflix(和其餘使用 Angular 2+ 的應用)使用基於 RxJS 的函數式功能。Facebook在 React 中使用純函數、高階函數和高級組件來開發 Facebook 和 Instagram,PayPal、KhanAcademy、和Flipkart使用 Redux 來進行狀態管理。

它們並不孤單:Angular、React、Redux 和 Lodash 是 JavaScript 生態系統中主要的框架和庫,同時它們都被函數式編程很深的影響到— 在 Lodash 和 Redux 中,明確地表達是爲了在實際的 JavaScript 應用中使用函數式編程模式。

「爲何是 JavaScript?」由於 JavaScript 是實際上大多數公司開發真實的軟件所使用的語言。不管你對它是愛是恨,JavaScript 已經取代了 Lisp 這個數十年來 「最受歡迎的函數式編程語言」。事實上,Haskell 更適合當今函數式編程概念的標準,可是人們並不使用它來開發實際應用。

在任什麼時候候,在美國都有近十萬的 JavaScript 工做需求,世界其餘地方也有數十萬的量。學習 Haskell 能夠幫助你很好的學習函數式編程,但學習 JavaScript 將會教會你在實際工做中開發應用。

App 正在吞食世界, web 正在吞食 app, 同時 JavaScript 正在吞食 web。

第三篇: 函數式開發者的 JavScript 介紹…

下一步

想更多的學習 JavaScript 的函數式編程?

Learn JavaScript with Eric Elliott,什麼,你還不是其中之一,out 了!

Eric Elliott「Programming JavaScript Applications」 (O’Reilly) 和 「Learn JavaScript with Eric Elliott」 的做者。他曾效力於 Adobe Systems, Zumba Fitness, he Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica 和其餘一些公司。

他和她的老婆(很漂亮)大部分時間都在舊金山灣區裏。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃

相關文章
相關標籤/搜索