|
編者按:本文轉載自公眾號 前端桃園
最開始接觸函數(shù)式編程的時候是在小米工作的時候,那個時候看老大以前寫的代碼各種 compose,然后一些 ramda 的一些工具函數(shù),看著很吃力,然后極力吐槽函數(shù)式編程,現(xiàn)在回想起來,那個時候的自己真的是見識短淺,只想說,'真香'。 最近在研究函數(shù)式編程,真的是在學習的過程中感覺自己的思維提升了很多,抽象能力大大的提高了,讓我深深的感受到了函數(shù)式編程的魅力。所以我打算后面用 5 到 8 篇的篇幅,詳細的介紹一下函數(shù)式編程的思想,基礎、如何設計、測試等。 今天這篇文章主要介紹函數(shù)式編程的思想。
總所周知 JavaScript 是一種擁有很多共享狀態(tài)的動態(tài)語言,慢慢的,代碼就會積累足夠的復雜性,變得笨拙難以維護。面向對象設計能幫我們在一定程度上解決這個問題,但是還不夠。 由于有很多的狀態(tài),所以處理數(shù)據(jù)流和變化的傳遞顯得尤為重要,不知道你們知道響應式編程與否,這種編程范式有助于處理 JavaScript 的異步或者事件響應??傊斘覀冊谠O計應用程序的時候,我們應該考慮是否遵守了以下的設計原則。
我這能這么跟你說,一旦你學會了函數(shù)式編程,這些問題迎刃而解,本來函數(shù)式編程就是這個思想,一旦你掌握了函數(shù)式,然后你再學習響應式編程那就比較容易懂了,這是我親身體會的。我之前在學 Rxjs 的時候是真的痛苦,說實話,Rxjs 是我學過最難的庫了,沒有之一。在經(jīng)歷過痛苦的一兩個月之后,有些東西還是不能融會貫通,知道我最近研究函數(shù)式編程,才覺得是理所當然。毫無夸張,我也盡量在后面的文章中給大家介紹一下 Rxjs,這個話題我也在公司分享過。 什么是函數(shù)式編程?簡單來說,函數(shù)式編程是一種強調以函數(shù)使用為主的軟件開發(fā)風格??吹竭@句我想你還是一臉懵逼,不知道函數(shù)式編程是啥,不要著急,看到最后我相信你會明白的。 還有一點你要記住,函數(shù)式編程的目的是使用函數(shù)來抽象作用在數(shù)據(jù)之上的控制流和操作,從而在系統(tǒng)中消除副作用并減少對狀態(tài)的改變。 下面我們通過例子來簡單的演示一下函數(shù)式編程的魅力。 現(xiàn)在的需求就是輸出在網(wǎng)頁上輸出 “Hello World”。 可能初學者會這么寫。
這個程序很簡單,但是所有代碼都是死的,不能重用,如果想改變消息的格式、內容等就需要重寫整個表達式,所以可能有經(jīng)驗的前端開發(fā)者會這么寫。
這樣確實有所改進,但是任然不是一段可重用的代碼,如果是要將文本寫入文件,不是非 HTML,或者我想重復的顯示 Hello World。 那么作為一個函數(shù)式開發(fā)者會怎么寫這段代碼呢?
解釋一下這段代碼,其中的 h1 和 echo 都是函數(shù),addToDom 很明顯也能看出它是函數(shù),那么我們?yōu)槭裁匆獙懗蛇@樣呢?看起來多了很多函數(shù)一樣。 其實我們是講程序分解為一些更可重用、更可靠且更易于理解的部分,然后再將他們組合起來,形成一個更易推理的程序整體,這是我們前面談到的基本原則。
可以看到我們是將一個任務拆分成多個最小顆粒的函數(shù),然后通過組合的方式來完成我們的任務,這跟我們組件化的思想很類似,將整個頁面拆分成若干個組件,然后拼裝起來完成我們的整個頁面。在函數(shù)式編程里面,組合是一個非常非常非常重要的思想。 好,我們現(xiàn)在再改變一下需求,現(xiàn)在我們需要將文本重復三遍,打印到控制臺。
可以看到我們更改了需求并沒有去修改內部邏輯,只是重組了一下函數(shù)而已。 可以看到函數(shù)式編程在開發(fā)中具有聲明模式。為了充分理解函數(shù)式編程,我們先來看下幾個基本概念。
聲明式編程函數(shù)式編程屬于聲明是編程范式:這種范式會描述一系列的操作,但并不會暴露它們是如何實現(xiàn)的或是數(shù)據(jù)流如何傳過它們。 我們所熟知的 SQL 語句就是一種很典型的聲明式編程,它由一個個描述查詢結果應該是什么樣的斷言組成,對數(shù)據(jù)檢索的內部機制進行了抽象。 我們再來看一組代碼再來對比一下命令式編程和聲明式編程。
可以看到命令式很具體的告訴計算機如何執(zhí)行某個任務。 而聲明式是將程序的描述與求值分離開來。它關注如何用各種表達式來描述程序邏輯,而不一定要指明其控制流或狀態(tài)關系的變化。 為什么我們要去掉代碼循環(huán)呢?循環(huán)是一種重要的命令控制結構,但很難重用,并且很難插入其他操作中。而函數(shù)式編程旨在盡可能的提高代碼的無狀態(tài)性和不變性。要做到這一點,就要學會使用無副作用的函數(shù)--也稱純函數(shù) 純函數(shù)純函數(shù)指沒有副作用的函數(shù)。相同的輸入有相同的輸出,就跟我們上學學的函數(shù)一樣,常常這些情況會產(chǎn)生副作用。
舉一個簡單的例子
這個函數(shù)就是不純的,它讀取了外部的變量,可能會覺得這段代碼沒有什么問題,但是我們要知道這種依賴外部變量來進行的計算,計算結果很難預測,你也有可能在其他地方修改了 counter 的值,導致你 increment 出來的值不是你預期的。 對于純函數(shù)有以下性質:
但是在我們平時的開發(fā)中,有一些副作用是難以避免的,與外部的存儲系統(tǒng)或 DOM 交互等,但是我們可以通過將其從主邏輯中分離出來,使他們易于管理。 現(xiàn)在我們有一個小需求:通過 id 找到學生的記錄并渲染在瀏覽器(在寫程序的時候要想到可能也會寫到控制臺,數(shù)據(jù)庫或者文件,所以要想如何讓自己的代碼能重用)中。
如果看不懂 curry (柯里化)的先不著急,這是一個對于新手來說比較難理解的一個概念,在函數(shù)式編程里面起著至關重要的作用。 可以看到函數(shù)式代碼通過較少這些函數(shù)的長度,將 showStudent 編寫為小函數(shù)的組合。這個程序還不夠完美,但是已經(jīng)可以展現(xiàn)出相比于命令式的很多優(yōu)勢了。
我們看到純函數(shù)的輸出結果是一致的,可預測的,相同的輸入會有相同的返回值,這個其實也被稱為引用透明。 引用透明引用透明是定義一個純函數(shù)較為正確的方法。純度在這個意義上表面一個函數(shù)的參數(shù)和返回值之間映射的純的關系。如果一個函數(shù)對于相同的輸入始終產(chǎn)生相同的結果,那么我們就說它是引用透明。 這個概念很容易理解,簡單的舉兩個例子就行了。
不可變數(shù)據(jù)不可變數(shù)據(jù)是指那些創(chuàng)建后不能更改的數(shù)據(jù)。與許多其他語言一樣,JavaScript 里有一些基本類型(String,Number 等)從本質上是不可變的,但是對象就是在任意的地方可變。 考慮一個簡單的數(shù)組排序代碼:
這段代碼看似沒什么問題,但是會導致在排序的過程中會產(chǎn)生副作用,修改了原始引用,可以看到原始的 arr 變成了 [1, 2, 3]。這是一個語言缺陷,后面會介紹如何克服。 總結
關于奇舞周刊《奇舞周刊》是360公司專業(yè)前端團隊「奇舞團」運營的前端技術社區(qū)。關注公眾號后,直接發(fā)送鏈接到后臺即可給我們投稿。
|
|
|