JS輕量級函數(shù)式編程實現(xiàn)XDM一
前言
盲猜一個:如果你有看過
《“類”設(shè)計模式和“原型”設(shè)計模式——“復(fù)制”和“委托”的差異》
這兩篇文章,你一定會對 JS 的【函數(shù)】有更多興趣!
皮一下,很舒服~ 沒錯!JS 就是輕量級的函數(shù)式編程!
拆解一下這句話,品味一下~
本瓜將借助《JavaScript 輕量級函數(shù)式編程》一書帶領(lǐng)你先透析它的落腳點函數(shù)式編程,然后再看看 JS 為什么被稱為是 “輕量的”!
此篇是《JS如何函數(shù)式編程》系列的第一篇,點贊??關(guān)注??持續(xù)追蹤!
FP 概覽
重要性
函數(shù)式編程(FP),不是一個新的概念,它幾乎貫穿了整個編程史。直到最近幾年,函數(shù)式編程才成為整個開發(fā)界的主流觀念。
函數(shù)式編程有完善且清晰的原則,一旦我們知道這些原則,我們將能更加快速地讀懂代碼,定位問題。這是為什么函數(shù)式編程重要的原因!
比如:你可能寫過一些命令式的代碼,像 if 語句和 for 循環(huán)這樣的語句。這些語句旨在精確地指導(dǎo)計算機如何完成一件事情。而聲明式代碼,以及我們努力遵循函數(shù)式編程原則所寫出的代碼,更專注于描述最終的結(jié)果。
函數(shù)式編程以另一種方式來思考代碼應(yīng)該如何組織才能使數(shù)據(jù)流更加明顯,并能讓讀者很快理解你的思想。
記住,你編寫的每一行代碼之后都要有人來維護,這個人可能是你的團隊成員,也可能是未來的你。
最初的函數(shù)
函數(shù)式編程不是僅僅用 function 這個關(guān)鍵詞來編程,就像面向?qū)ο缶幊滩粌H僅是用了對象就算是。
函數(shù)的真正意義是什么?
回到最初的起點,我們心中的函數(shù)一定是這樣的:
f(x) = 2x2 + 3,這是數(shù)學(xué)上真正的函數(shù)。那這和函數(shù)式編程有什么關(guān)系呢?
函數(shù)的本質(zhì)是【映射】。以一個優(yōu)雅的方式來描述一組值和另一組值的映射關(guān)系,即函數(shù)的輸入值與輸出值之間的關(guān)聯(lián)關(guān)系。
在編程中,它或許有許多個輸入值,或許沒有。它或許有一個輸出值( return 值),或許沒有。
如果你計劃使用函數(shù)式編程,你應(yīng)該盡可能多地使用函數(shù),而不是程序。你所有編寫的 function 應(yīng)該接收輸入值,并且返回輸出值。(這么做的原因是多方面的,后續(xù)會一一介紹)
這里,輸入值就是函數(shù)傳參,輸出值就是return的東西。(如果你沒有 return 值,或者你使用 return;,那么則會隱式地返回 undefined 值。)
思考“return”
思考以下代碼:
function foo(x) { if (x > 10) return x + 1; var y = x / 2; if (y > 3) { if (x % 2 == 0) return x; } if (y > 1) return y; return x; }
foo(2) 返回什么? foo(4) 返回什么? foo(8), foo(12) 呢?
將上述代碼變形,請思考:
function foo(x) { var retValue; if (retValue == undefined && x > 10) { retValue = x + 1; } var y = x / 2; if (y > 3) { if (retValue == undefined && x % 2 == 0) { retValue = x; } } if (retValue == undefined && y > 1) { retValue = y; } if (retValue == undefined) { retValue = x; } return retValue; }
這樣寫會更容易理解嗎?
在每個 retValue 可以被設(shè)置的分支, 這里都有個守護者以確保 retValue 沒有被設(shè)置過才執(zhí)行。(?)
相比在函數(shù)中提早使用 return,我們更應(yīng)該用常用的流控制( if 邏輯 )來控制 retValue 的賦值。到最后,我們 return retValue。
用 return 來實現(xiàn)流控制,會創(chuàng)造更多的隱含意義。
再來思考以下代碼:
// 通過一個函數(shù)修改變量 y 的值 var y; function foo(x) { y = (2 * Math.pow( x, 2 )) + 3; } foo( 2 ); y;
但是我們也可以這樣寫:
function foo(x) { return (2 * Math.pow( x, 2 )) + 3; } var y = foo( 2 ); y;
后一個版本更有優(yōu)勢嗎?
答案是肯定的:有!
后一個版本中的 return 表示一個顯式輸出,而前者的 y 賦值是一個隱式輸出。
通常,開發(fā)人員喜歡顯式模式而不是隱式模式。
為什么說后者 return 出來的就是顯式的?而前者的 y 賦值是隱式的?
這個例子可以給你答案:
function sum(list) { var total = 0; for (let i = 0; i < list.length; i++) { if (!list[i]) list[i] = 0; // list 使用了 nums 的引用,不是對 [1,3,9,..] 的值復(fù)制,而是引用復(fù)制。 total = total + list[i]; } return total; } var nums = [ 1, 3, 9, 27, , 84 ]; sum( nums ); // 124
這段代碼,除了 return 的輸出,還有沒有其它輸出可能改變到函數(shù)外部參數(shù) nums 的值?
是有的!就是在注釋的一行,我們無意中改變了 nums 。
console.log(nums) // [1, 3, 9, 27, 0, 84]
JS 對數(shù)組、對象和函數(shù)都使用引用和引用復(fù)制,我們可以很容易地從函數(shù)中創(chuàng)建輸出,即使是無心的。
這個隱式函數(shù)輸出在函數(shù)式編程中有一個特殊的名稱:副作用。
沒有副作用的函數(shù)也有一個特殊的名稱:純函數(shù),這個概念十分重要,后面對有更多討論!
高階函數(shù)
一個函數(shù)如果可以接受或返回一個甚至多個函數(shù),它被叫做高階函數(shù)。
其中最強大的就是:【閉包】。
我們將在的后續(xù)舉例中大量使用閉包。它可能是所有函數(shù)式編程中最重要的基礎(chǔ)。
此處舉一小例:
假設(shè)你需要將兩個值相加,一個你已經(jīng)知道,另一個還需要后面才能知道,你可以使用閉包來記錄第一個輸入值:
function makeAdder(x) { return function sum(y){ return x + y; }; } //我們已經(jīng)分別知道作為第一個輸入的 10 和 37 var addTo10 = makeAdder( 10 ); var addTo37 = makeAdder( 37 ); // 緊接著,我們指定第二個參數(shù) addTo10( 3 ); // 13 addTo10( 90 ); // 100 addTo37( 13 ); // 50
這種在連續(xù)函數(shù)調(diào)用中指定輸入,是函數(shù)式編程中非常普遍的形式。
它可分為兩類:偏函數(shù)應(yīng)用和柯里化。后續(xù)會展開。
具名函數(shù)
我們提倡要用具名函數(shù),而不是匿名函數(shù),這更有利于我們語義化代碼,比如getPreferredName(..),操作意圖很明確,并且可以很好的回溯問題,防止出現(xiàn) (anonymous function) 。
但是 => 箭頭函數(shù)除外,箭頭函數(shù)還是得有效利用。
=> 箭頭函數(shù)令人興奮的地方在于它幾乎完全遵循函數(shù)的數(shù)學(xué)符號,特別是像 Haskell 這樣的函數(shù)式編程語言。它能簡化、優(yōu)化代碼片段中的空間。
丟掉“this"
JavaScript 中的 this 綁定規(guī)則是真的難記,好消息是我們將把 this 丟棄掉,不去理會它。
這樣做的內(nèi)核原因是:this 是函數(shù)的一個隱式的輸入?yún)?shù)。前面我們提到通常,開發(fā)人員喜歡顯式模式而不是隱式模式。,這樣的隱式輸入違背了我們的原則。
階段小結(jié)
函數(shù)是強大的!
- 我們學(xué)習(xí)函數(shù)式編程的全部理由是為了書寫更具可讀性的代碼。
- 程序中,函數(shù)不僅僅是一個語句或者操作的集合,而是需要一個或多個輸入(理想情況下只需一個?。┖鸵粋€輸出。開發(fā)人員喜歡顯式輸入輸出而不是隱式輸入輸出。
- 函數(shù)內(nèi)部的函數(shù)可以取到閉包外部變量,并記住它們以備日后使用。這是所有程序設(shè)計中最重要的概念之一,也是函數(shù)式編程的基礎(chǔ)。
- 要警惕匿名函數(shù),特別是 => 箭頭函數(shù)。雖然在編程時用起來很方便,但是會對增加代碼閱讀的負擔(dān)。
- 別用 this 敏感的函數(shù)。這不需要理由。
以上就是JS函數(shù)式編程實現(xiàn)XDM一的詳細內(nèi)容,更多關(guān)于JS輕量級函數(shù)式編程XDM的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript實現(xiàn)一個Promise隊列小工具
這篇文章主要介紹了JavaScript實現(xiàn)一個Promise隊列小工具,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-08-08微信小程序 循環(huán)及嵌套循環(huán)的使用總結(jié)
這篇文章主要介紹了微信小程序 循環(huán)及嵌套循環(huán)的使用總結(jié)的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09