摘要:最近在看,順便看了一些函數(shù)式編程,然后半個(gè)國慶假期就沒有了。最開始接觸函數(shù)式編程的時(shí)候,第一個(gè)接觸的概念就是高階函數(shù),和柯里化。所以我覺得最開始學(xué)習(xí)函數(shù)式編程最好先了解一些相關(guān)概念和思想會(huì)比較好。
最近在看Typescript,順便看了一些函數(shù)式編程,然后半個(gè)國慶假期就沒有了。做個(gè)筆記,分幾個(gè)部分寫吧。
最開始接觸函數(shù)式編程的時(shí)候,第一個(gè)接觸的概念就是高階函數(shù),和柯里化。咋一看,這不就是長期用來講作用域的demo嗎?我在日常也有用啊,有啥嗎?
其實(shí)呢,設(shè)計(jì)模式或則編程范式往往不在于技巧,而在于思想。函數(shù)式編程就是一種編程的范式,并不在于技巧多么叼,而在于它的思想。其次才是由設(shè)計(jì)思想才衍生出來的技巧,技巧往往而言是服務(wù)于思想的。所以我覺得最開始學(xué)習(xí)函數(shù)式編程最好先了解一些相關(guān)概念和思想會(huì)比較好。
函數(shù)是一等公民(first class)如果理解直接看為一等公民的好處好處。
其實(shí)說函數(shù)式一等公民的意思就是說函數(shù)和其他“公民”具有相同的屬性。就像任何一種數(shù)據(jù)類型,它能夠被存儲(chǔ)在數(shù)組中,能夠作為函數(shù)的參數(shù),能夠賦值給變量:
const hello = (name) => (`Hello ${name}!`) const sayHello = hello; // 作為變量 const helloArray = [hello, sayHello]
上面的代碼沒有什么意義,只是表達(dá)函數(shù)在JavaScript中是一等公民,和一個(gè)值一樣。
函數(shù)是一等公民的好處拿一個(gè)callback的例子來講,比如你用fetch發(fā)個(gè)請求時(shí):
fetch("getPostLink") .then(res => renderPosts(res)) .catch(err => handleError(error))
上面其實(shí)可以直接傳遞一個(gè)函數(shù)作為回調(diào),加一層包裹其實(shí)沒有必要:
fetch("getPostLink").then(renderPosts).catch(handleError)
多一層函數(shù)的包裹并沒有任何意義,完全是多余的代碼。再看一個(gè)例子:
const postController = { find(postId) { return Db.find(postId) }, delete(postId) { return Db.delete(postId) }, ... }
上面的代碼其實(shí)就是聚合一些功能作為一個(gè)對象,但是多加了一層的函數(shù),也是沒有必要的,在閱讀的時(shí)候到會(huì)增加復(fù)雜度,其實(shí)postController.find === Db.find,所以完全沒有再去包裹一層函數(shù):
const postController = { find: Db.find, delete: Db.delete, ... }
上面的代碼是不是更表意,然而如果js的函數(shù)不能像值一樣傳遞,上面的簡寫都是不可能的。上面的代碼其實(shí)還有一個(gè)好處,你不用去糾結(jié)如何命名在兩層函數(shù)之間的參數(shù)了。這種風(fēng)格代碼是符合Pointfree的,我們后面要介紹。另外,函數(shù)式編程是操作函數(shù)的,所以函數(shù)是一等公民也是函數(shù)式的基石,基本上如果js不支持這一項(xiàng),函數(shù)式根本玩不轉(zhuǎn)。
純函數(shù)讓我舉一個(gè)例子,大家在小學(xué)多學(xué)過一元一次方程吧:
f(x) = ax+b
這就是一個(gè)純函數(shù),一個(gè)輸入然后返回一個(gè)輸出。所有的東西都是圍繞輸入的,一個(gè)輸入只可能返回一個(gè)輸出,然后對任何其他沒在作用域中的變量沒有任何操作。
更書面的解釋:一個(gè)純函數(shù)一個(gè)輸入永遠(yuǎn)都只有一個(gè)同樣的輸出,然后不會(huì)產(chǎn)生任何副作用。副作用是啥我們一會(huì)兒再說。
不純的函數(shù)通常不純的函數(shù)分為兩類,一種是會(huì)改變輸入的:
const numbers = [1, 2, 3] // 純函數(shù) numbers.slice(0, 3) // [1, 2, 3] numbers.slice(1, 3) // [2, 3] numbers.slice(0, 2) // [1, 2] // 不純的函數(shù) numbers.splice(0, 3) // [1, 2, 3] numbers.splice(0, 3) // []
上面中在numbers這個(gè)數(shù)組上面的兩個(gè)方法,slice是純函數(shù)。而splice則不是純函數(shù),它會(huì)改變輸入的數(shù)值。做了額外的事。
另外一種是對函數(shù)以外的狀態(tài)有依賴的:
let endpointForYoung = 18 // 不純的函數(shù) const checkYoungPeople = age => age <= endpointForYoung // 純函數(shù) const checkYoungPeople = age => { const endpointForYoung = 18 return age <= endpointForYoung }
像上面的函數(shù),第一個(gè)就是不純的,他依賴的作用域之外的一個(gè)變量,一旦這個(gè)變量改變,這個(gè)函數(shù)返回的值就會(huì)跟著改變。
副作用副作用就是在函數(shù)計(jì)算過程中,對函數(shù)外的狀態(tài)進(jìn)行更改或則與函數(shù)外狀態(tài)進(jìn)行交互的行為。首先副作用會(huì)導(dǎo)致函數(shù)不純,是程序有不可控的依賴,不便于管理。但是,副作用是不可消除的,在正常的編程活動(dòng)中是必然伴隨著副作用的。所以在面對副作用時(shí),問題不是如何消除副作用,而是如何管理副作用。這個(gè),會(huì)在我們講解范疇論相關(guān)概念的時(shí)候再深入。
正常編程活動(dòng)中會(huì)引入的副作用有這些:
文件讀寫操作
增刪改查數(shù)據(jù)庫
http 請求
打印log
獲得用戶輸入
獲取dom元素
當(dāng)然不限于上面這幾種,還有很多行為都帶有副作用。
純函數(shù)的好處能緩存
純函數(shù)的每次輸入和輸出都是沒有狀態(tài)的,所以結(jié)果都一樣,能夠被緩存在任何地方而不會(huì)造成錯(cuò)誤。
可移植性,自文檔
// 不純函數(shù) const signUp = (attrs) => { const user = saveUser(attrs); welcomeUser(user); }; // 純函數(shù) const signUp = (Db, Email, attrs) => () => { const user = saveUser(Db, attrs); welcomeUser(Email, user); };
第二個(gè)signUp依賴是從上傳遞的,所以能直觀的看出saveUser需要Db,welcomeUser還需要Email。在不純的函數(shù)中你很難在調(diào)用的時(shí)候知道他的依賴,你需要查看代碼,才能搞清楚,“哦,原來還用了Db存了波數(shù)據(jù)啊。”
依賴作為參數(shù)傳入,也很容易的在移植到其他場景使用,畢竟函數(shù)只是功能,針對不同場景操作不同的數(shù)據(jù)。
易于測試
寫單測的時(shí)候,最麻煩的就是如何mock數(shù)據(jù)。通常有兩類數(shù)據(jù)最難mock,第一個(gè)是全局變量,比如document,另外一類是import進(jìn)來的依賴,對于這兩種,雖然在一些測試套件中有現(xiàn)成的工具庫去mock。但是,都是很詭異的方式。
而如果是函數(shù)式的話,你測的就是一個(gè)輸入一個(gè)輸出,沒有外部的影響,是非常容易測試的。
并行代碼
純函數(shù)都是沒有狀態(tài)的,那即使跑在多臺(tái)機(jī)器多個(gè)進(jìn)程,每個(gè)單元相互之間是沒有耦合關(guān)系的。
Pointfree大家可以看阮老師的這篇博客了解一下:http://www.ruanyifeng.com/blo...
我只扯一下Pointfree風(fēng)格代碼的好處:
正如上面所說的中間變量沒有意義,不需要給變量命名
代碼更簡潔,精煉,不用將過多的狀態(tài)暴露給消費(fèi)者
當(dāng)然也有人認(rèn)為其將太多的狀態(tài)隱藏了,初讀代碼很難理解,只有看了具體函數(shù)實(shí)現(xiàn)功能才能知道真正的意圖,對于代碼的可讀性而言,很糟糕。
這里有一處在hacker news上的討論P(yáng)oint-Free style: What is it good for?。另外這還有一篇具體使用場景的文章https://medium.freecodecamp.org/how-point-free-composition-will-make-you-a-better-functional-programmer-33dcb910303a。有興趣的小伙伴可以自己看看。
添加一張我筆記未整理的腦圖。
OK,下一篇介紹一下函數(shù)組合和柯里化。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/98227.html
摘要:我稱之為輕量級(jí)函數(shù)式編程。序眾所周知,我是一個(gè)函數(shù)式編程迷。函數(shù)式編程有很多種定義。本書是你開啟函數(shù)式編程旅途的絕佳起點(diǎn)。事實(shí)上,已經(jīng)有很多從頭到尾正確的方式介紹函數(shù)式編程的書了。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson - 《You-Dont-Know-JS》作者 譯者團(tuán)隊(duì)(排名不分先后):阿希、blueken、brucecham、...
摘要:本書主要探索函數(shù)式編程的核心思想。我們在中應(yīng)用的僅僅是一套基本的函數(shù)式編程概念的子集。我稱之為輕量級(jí)函數(shù)式編程。通常來說,關(guān)于函數(shù)式編程的書籍都熱衷于拓展閱讀者的知識(shí)面,并企圖覆蓋更多的知識(shí)點(diǎn)。,本書統(tǒng)稱為函數(shù)式編程者。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson?。 禮ou-Dont-Know-JS》作者 譯者團(tuán)隊(duì)(排名不分先后)...
摘要:在學(xué)習(xí)的過程中時(shí)常會(huì)聽到一個(gè)名次函數(shù)式編程,那么究竟什么是函數(shù)式編程,函數(shù)式編程又有什么優(yōu)點(diǎn),這就在這篇博客進(jìn)行一個(gè)簡單的總結(jié)吧主要內(nèi)容函數(shù)式編程的概念函數(shù)式編程的優(yōu)點(diǎn)與示例什么是函數(shù)式編程首先,我們放下編程的概念,我們來看函數(shù)。 在學(xué)習(xí) JS 的過程中時(shí)常會(huì)聽到一個(gè)名次——函數(shù)式編程,那么究竟什么是函數(shù)式編程,函數(shù)式編程又有什么優(yōu)點(diǎn),這就在這篇博客進(jìn)行一個(gè)簡單的總結(jié)吧~ 主要內(nèi)容: 函...
摘要:為了盡可能提升互通性,已經(jīng)成為函數(shù)式編程庫遵循的實(shí)際標(biāo)準(zhǔn)。與輕量級(jí)函數(shù)式編程的概念相反,它以火力全開的姿態(tài)進(jìn)軍的函數(shù)式編程世界。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 關(guān)于譯者:這是一個(gè)流淌著滬江血液的純粹工程:認(rèn)真,是 HTML 最堅(jiān)實(shí)的梁柱;分享,是 CSS 里最閃耀的一瞥;總結(jié),...
摘要:所以我覺得函數(shù)式編程領(lǐng)域更像學(xué)者的領(lǐng)域。函數(shù)式編程的原則是完善的,經(jīng)過了深入的研究和審查,并且可以被驗(yàn)證。函數(shù)式編程是編寫可讀代碼的最有效工具之一可能還有其他。我知道很多函數(shù)式編程編程者會(huì)認(rèn)為形式主義本身有助于學(xué)習(xí)。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson?。 禮ou-Dont-Know-JS》作者 關(guān)于譯者:這是一個(gè)流淌著滬江血液...
閱讀 1397·2023-04-26 02:38
閱讀 1013·2023-04-25 20:13
閱讀 3651·2021-11-19 11:31
閱讀 2454·2019-08-30 15:55
閱讀 2785·2019-08-30 14:11
閱讀 3222·2019-08-30 13:45
閱讀 1438·2019-08-29 18:41
閱讀 1227·2019-08-29 16:18