【譯】為什么學(xué)習(xí) Javascript 函數(shù)式編程

這是軟件編寫系列文章中關(guān)于學(xué)習(xí)函數(shù)式編程和使用 ES6 構(gòu)建軟件技術(shù)的部分,敬請關(guān)注,更多的相關(guān)內(nèi)容??
Start over at Part 1 | Next >

忘記你對 Javascript 的任何認知并且用初學(xué)者的思想去閱讀這篇文章。為了幫助你,我們將從頭開始復(fù)習(xí) Javascript 基礎(chǔ)就像從未見過它一樣。假如你是個初學(xué)者,那么你是幸運的。所有的新概念都會被解釋,但是不要指望得到太多的呵護。

假如你是經(jīng)驗豐富的開發(fā)者并且已經(jīng)熟悉 Javascript 或者其他的純函數(shù)語言,可能你會覺得用 Javascript 去探索函數(shù)式編程是一個有趣的選擇。把其他的想法都放置一旁并且用開放的思想去體會,你將會發(fā)現(xiàn)一種聞所未聞的 Javascript 編程境界。

由于本文被稱為 “軟件編寫”,并且函數(shù)式編程是構(gòu)成軟件的明顯方式(使用函數(shù)組成,高階函數(shù)等等),你可能在想為什么我沒有談?wù)揌askell,ClojureScript 或者 Elm,而是 Javascript。

Javascript 擁有函數(shù)式編程所需要的最重要的功能:

1、一等函數(shù):使用函數(shù)作為數(shù)據(jù)的能力:傳遞函數(shù)作為參數(shù),返回函數(shù),分配函數(shù)作為變量和對象屬性。這個屬性可以是部分應(yīng)用、柯里化和組成的高階函數(shù)。

2、匿名函數(shù)和簡潔的 lambda 語法x => x * 2 在 Javascriot 中是有效地函數(shù)表達式。簡潔的 lambda 語法讓它作為高階函數(shù)簡單地運行。

3、閉包:閉包是一個函數(shù)和它的詞匯環(huán)境形成的捆綁。閉包在函數(shù)創(chuàng)建時形成。當(dāng)函數(shù)被定義在另一個函數(shù)中時,閉包可以訪問在外部函數(shù)綁定的變量,即使外部函數(shù)已經(jīng)銷毀。閉包就是部分應(yīng)用如何去獲取固定參數(shù),而固定參數(shù)是在返回函數(shù)中閉包環(huán)境中綁定的參數(shù)。比如 add(1)(2),1 就是返回函數(shù)add(1)中的固定參數(shù)。

Javascript 失去了什么

Javascript 是一門多范式語言,意味著它支持多種不同風(fēng)格的編寫代碼。Javascript 支持的其他風(fēng)格有:面向過程編程(比如 C),面向?qū)ο缶幊毯秃瘮?shù)式編程。多范式的語言優(yōu)勢就是,在面向?qū)ο蠛兔嫦蜻^程中意味著幾乎所有事物都是可變的。

突變是發(fā)生在數(shù)據(jù)結(jié)構(gòu)中具體位置上的改變。舉個例子:

const foo = {
  bar: 'baz'
};
foo.bar = 'qux'; // mutation

對象是經(jīng)常需求被改變的,所以它們的屬性可以被方法更新。在面向過程編程中,大多數(shù)數(shù)據(jù)結(jié)構(gòu)都是可變的,去實現(xiàn)有效地操作對象和數(shù)組。

以下是一些函數(shù)式語言擁有而 Javascript 沒有的功能。
1、純度:在一些函數(shù)式編程語言中,純度是被強制執(zhí)行的,不允許使用有副作用的表達式。

2、不變性:一些函數(shù)式編程語言中是禁止改變。而不是去變更現(xiàn)有的數(shù)據(jù)結(jié)構(gòu),比如數(shù)組、對象或者表達式去創(chuàng)建新的數(shù)據(jù)結(jié)構(gòu)。這樣的做法可能聽起來效率低下,但是大多數(shù)的函數(shù)式語言在結(jié)構(gòu)共享的“罩”中使用 trie 數(shù)據(jù)結(jié)構(gòu),舊對象和新對象都共享著相同的數(shù)據(jù)。

3、遞歸:遞歸是函數(shù)調(diào)用自己去實現(xiàn)迭代的能力。在許多函數(shù)式編程語言中,遞歸是迭代的唯一方式,其中沒有循環(huán)語法比如:for、while 或者 do。

純度:在 Javascript中,純度必須通過管理去實現(xiàn)。假如之前你不同使用純函數(shù)去開發(fā)大多數(shù)應(yīng)用的話,那么你就沒有使用函數(shù)式的編程范式。不幸的是,在 Javascript 中非常容易就偶然創(chuàng)建和使用非純函數(shù)。

不變性:在純函數(shù)語言中,不變性經(jīng)常被強制執(zhí)行。Javascript 中缺少在大多數(shù)函數(shù)式語言中使用的高效、不變的 trie 數(shù)據(jù)結(jié)構(gòu),但是可以通過一些 js 庫區(qū)實現(xiàn)比如:Immutable.js 和 Mori。我希望未來的
ECMAScript 版本可以擁抱不變的數(shù)據(jù)結(jié)構(gòu)。

其中的一些標志基于了希望,比如在 ES6 中新增的 const 關(guān)鍵字,使用 const 定義的變量不能重新賦值為不同的值,重要的是理解 const 并不意味著一個不變的值。

一個 const 對象不能被重新賦值為完全不同的對象,但是這個對象的屬性可以被改變。Javascript 同樣擁有能力去freeze()對象,但是對象只能凍結(jié)在頂級的屬性,這就意味著一個嵌套對象的屬性仍然可以發(fā)生改變。換句話來說,我們想要看到在 Javascript 規(guī)范中真正的綜合不變性還需要很長的路要走。

遞歸:Javascript 支持遞歸但是大多數(shù)函數(shù)式編程語言擁有叫做尾部調(diào)用優(yōu)化的功能,就是允許遞歸函數(shù)重用堆棧去實現(xiàn)遞歸調(diào)用。

沒有尾部調(diào)用優(yōu)化,調(diào)用堆棧將會變得沒有限制和導(dǎo)致棧溢出,Javascript 在 ES6 規(guī)范中新增了有界形式的尾部調(diào)用優(yōu)化。不幸的是,只有一種主流的瀏覽器支持它,優(yōu)化只是部分實現(xiàn)并且在 Babel 中去除掉了(Babel 是當(dāng)下最流行的 Javascript 標準編譯器,使用在舊瀏覽器中將 ES6 轉(zhuǎn)化為 ES5)。

總結(jié)一下:在大的迭代中使用遞歸是不安全的,即使在尾部謹慎地調(diào)用函數(shù)。

Javascript 擁有什么特性是純函數(shù)式語言所缺少的

一個純粹主義者會告訴你 Javascript 的不變性是主要的缺點,但是引起的副作用和突變在某些情況下是有益的。事實上,創(chuàng)建一個有用的現(xiàn)代應(yīng)用并且沒有副作用是不可能的。純函數(shù)式語言比如 Haskell 使用副作用,使用 monads 將有副作用的純函數(shù)偽裝成純函數(shù),盡管使用 monads 包會有不純的副作用。

monads 的問題是盡管它們使用起來足夠簡單,但是跟一個不熟悉的人去解釋就像是對牛彈琴。

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

是的,這是在調(diào)侃這個有趣的點,在之前的引用中,對于 monads 的解釋對比最初的版本是進行了簡化,原文是這樣的:

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

盡管這樣,我還是認為沒有必要害怕 monads。最好的學(xué)習(xí)方式不是去讀大量的書或者相關(guān)主題的博客文章,而是投入其中并開始使用它們。對于大多是的函數(shù)式編程語言來說,不可思議的學(xué)識名詞比概念更加難以理解,相信我,你不必用過理解 Saunders Mac Lane 來理解函數(shù)式編程。

盡管它不是絕對使用于每種編程風(fēng)格,Javascript 是為了適應(yīng)不同編程風(fēng)格和背景的工程師使用而設(shè)計出來的。

根據(jù) Brendan Eich 所言,在設(shè)計之初,網(wǎng)景有意地去支持兩類開發(fā)者。

使用 c++ 或者 Java 編寫組件的;業(yè)余或者職業(yè)編寫嵌入 html 腳本的。

本來,網(wǎng)景公司有意地去支持兩種不同的語言,這門腳本語言可能會類似于 Scheme(Lisp 的一種方言)。 Brendan Eich:

我被招聘到網(wǎng)景公司就是為了在瀏覽器中去 "實現(xiàn) Scheme"

Javascript 應(yīng)該是一門新的語言。

上級工程管理部門的要求是這門語言必須看起來像 Java,然后就排除了 Perl,Python,Tcl 以及 Scheme。

所以,Brendan Eich 的想法是這樣的:
1、在瀏覽器中的 Scheme
2、看起來像 Java

它最終成為了一個大雜燴。

我不覺得驕傲,但是我很高興我選擇了 Scheme 的一等函數(shù)和 Self-ish 的原型作為主要的組成部分。由于 Java 的影響,尤其是 y2k 和 Date 的 Bug 以及對象的區(qū)別,就感到不幸了。

我列出了“不好的”類 Java 特性,最終實現(xiàn)了 Javascript。
1、構(gòu)造函數(shù)和 new 關(guān)鍵字,與工廠函數(shù)不同的調(diào)用方法和語義
2、單祖先 class 關(guān)鍵字擴展作為繼承的主要機制
3、用戶傾向于將 class 看做是它的靜態(tài)類型(其實不然)

我的建議是:避免使用這些。

值得慶幸的是, Javascript 成為了一門這么棒的語言,因為它證明了腳本方式勝過了建立在組件之上的方式(今天,Java、Flash 和 ActiveX 擴展都不被大多數(shù)的瀏覽器支持)

最終我們實現(xiàn)了一種被瀏覽器直接支持的語言:Javascript

這意味著瀏覽器更加輕便和穩(wěn)定,因為他們只需要支持一種語言:Javascript。你可能會認為 WebAssembly 是一個例外,但是設(shè)計 WebAssembly 的其中一個目的就是用兼容的抽象語法樹來共享 JavaScript 的語言綁定(AST)。事實上,最早的把 WebAssembly 編譯成 JavaScript 的子集的示范是 ASM.js。

作為 web 平臺唯一的標準通用語言,在軟件歷史上掀起了最大的語言熱潮:

app 吞噬了世界,web 吞噬了app,Javascript 吞噬了 web。

通過多平臺的調(diào)查,Javascript 是世界上最流行的編程語言。

但是 Javascript 不是實踐函數(shù)式編程的理想工具,但是它是為分布式團隊去建立大型應(yīng)用的好工具,因為不同的團建在開發(fā)項目上會有不同的想法。

一些團隊致力于腳本化,那么命令式編程就會顯得特別有用。一些則會專注于抽象架構(gòu),其中會保留的一些面向?qū)ο蟮姆椒ㄒ膊诲e。還有一些會擁抱函數(shù)式編程,使用純函數(shù)來確保穩(wěn)定性、可測試性和管理應(yīng)用狀態(tài)以便減少用戶反饋,團隊成員都使用同一種語言,似的他們更好地交換想法,互相學(xué)習(xí)和協(xié)作開發(fā)。

在 Javascript 中,所有的這些想法都可以實現(xiàn),使得更多的人開始擁抱 Javascript,這就誕生了世界上最大的開源包管理工具 (as of February, 2017), npm.

Javascript 真正的優(yōu)勢是生態(tài)系統(tǒng)的思想的多樣性和用戶。它可能不是函數(shù)式編程純粹主義者心中絕對理想的語言,但它是你可以想象的工作在不同平臺的人共同合作的理想語言,比如說 Java、Lisp 或者 C。JavaScript 也許并不對有這些背景的用戶完全友好,但是這些人很樂意學(xué)習(xí)這門語言并迅速投入生產(chǎn)。

我同意 Javascript 不是對函數(shù)式編程開發(fā)者最好的語言。但是,沒有任何一門語言可以說它們可以被所有人去使用,正如 ES6 所說那樣:Javascript 可以更好地滿足函數(shù)式開發(fā)者的需求,相比于拋棄 JavaScript 和世界上幾乎每家公司都使用的令人難以置信的生態(tài)系統(tǒng),為什么不擁抱它,把它變成一個更適合軟件組合化的語言?

他們并不是孤獨的,Angular,React,Redux 和 Lodash 是 Javascript 生態(tài)系統(tǒng)中主要的框架和庫,并且對函數(shù)式編程產(chǎn)生了重大的影響 -- 在 Lodash 和 redux 中,明確地表示了可以在真實的 Javascript 環(huán)境中使用函數(shù)式編程模式。

“為什么是 Javascript?” 因為 Javascript 被大多數(shù)公司用來開發(fā)實際應(yīng)用。無視是喜歡還是討厭它,Javascript 已經(jīng)從 Lisp 手中接手了數(shù)十年以來 “最流行的函數(shù)式語言” 的名號。事實上,Haskell 更適合當(dāng)今函數(shù)式編程概念的標準,但是人們并不使用它來開發(fā)實際應(yīng)用。

在任何時候,在美國都有近十萬的 JavaScript 工作需求,世界其他地方也有數(shù)十萬的量。學(xué)習(xí) Haskell 可以幫助你很好的學(xué)習(xí)函數(shù)式編程,但學(xué)習(xí) JavaScript 將會教會你在實際工作中開發(fā)應(yīng)用。

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

**第三部分: 一個函數(shù)式開發(fā)者介紹 Javascript **

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容