亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JS函數(shù)式編程之純函數(shù)、柯里化以及組合函數(shù)

 更新時(shí)間:2023年01月08日 11:55:30   作者:虛幻私塾  
這篇文章主要介紹了JS函數(shù)式編程之純函數(shù)、柯里化以及組合函數(shù),文章對三個(gè)函數(shù)進(jìn)行分析講解,內(nèi)容也很容易理解,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

前言

函數(shù)式編程(Functional Programming),又稱為泛函編程,是一種編程范式。

早在很久以前就提出了函數(shù)式編程這個(gè)概念了,而后面一直長期被面向?qū)ο缶幊趟y(tǒng)治著,最近幾年函數(shù)式編程又回到了大家的視野中,JavaScript是一門以函數(shù)為第一公民的語言,必定是支持這一種編程范式的。

下面就來談?wù)凧avaScript函數(shù)式編程中的核心概念純函數(shù)、柯里化以及組合函數(shù)。

純函數(shù)

純函數(shù)的概念

對于純函數(shù)的定義,維基百科中是這樣描述的:在程序設(shè)計(jì)中,若函數(shù)符合以下條件,那么這個(gè)函數(shù)被稱之為純函數(shù)。

  • 此函數(shù)在相同的輸入值時(shí),需產(chǎn)生相同的輸出;
  • 函數(shù)的輸入和輸出值以外的其他隱藏信息或狀態(tài)無關(guān),也和由I/O設(shè)備產(chǎn)生的外部輸出無關(guān)
  • 該函數(shù)不能有語義上可觀察的函數(shù)副作用,諸如“觸發(fā)事件”,使輸出設(shè)備輸出,或更改輸出值以外物件的內(nèi)容等;

對以上描述總結(jié)就是:

  • 對于相同的輸入,永遠(yuǎn)會(huì)得到相同的輸出;
  • 在函數(shù)的執(zhí)行過程中,沒有任何可觀察的副作用;
  • 同時(shí)也不依賴外部環(huán)境的狀態(tài);

副作用

上面提到了一個(gè)詞叫“副作用”,那么什么是副作用呢?

  • 通常我們所說的副作用大多數(shù)是指藥會(huì)產(chǎn)生的副作用;
  • 而在計(jì)算機(jī)科學(xué)中,副作用指在執(zhí)行一個(gè)函數(shù)時(shí),除了得到函數(shù)的返回值以外,還在函數(shù)調(diào)用時(shí)產(chǎn)生了附加的影響,比如修改了全局變量的狀態(tài),修改了傳入的參數(shù)或得到了其它的輸出內(nèi)容等;

純函數(shù)案例

  • 編寫一個(gè)求和的函數(shù)sum,只要我們輸入了固定的值,sum函數(shù)就會(huì)給我們返回固定的結(jié)果,且不會(huì)產(chǎn)生任何副作用。
function sum(a, b) {
 return a + b
}

const res = sum(10, 20)
console.log(res) // 30
  • 以下的sum函數(shù)雖然對于固定的輸入也會(huì)返回固定的輸出,但是函數(shù)內(nèi)部修改了全局變量message,就認(rèn)定為產(chǎn)生了副作用,不屬于純函數(shù)。
let message = 'hello'
function sum(a, b) {
 message = 'hi'
 return a + b
}
  • 在JavaScript中也提供了許多的內(nèi)置方法,有些是純函數(shù),有些則不是。像操作數(shù)組的兩個(gè)方法slice和splice。

1.slice方法就是一個(gè)純函數(shù),因?yàn)閷τ谕粋€(gè)數(shù)組固定的輸入可以得到固定的輸出,且沒有任何副作用;

const nums = [1, 2, 3, 4, 5]
const newNums = nums.slice(1, 3)
console.log(newNums) // [2, 3]
console.log(nums) // [ 1, 2, 3, 4, 5 ]

2.splice方法不是一個(gè)純函數(shù),因?yàn)樗淖兞嗽瓟?shù)組nums;

const nums = [1, 2, 3, 4, 5]
const newNums = nums.splice(1, 3)
console.log(newNums) // [ 2, 3, 4 ]
console.log(nums) // [ 1, 5 ]

柯里化

柯里化的概念

對于柯里化的定義,維基百科中是這樣解釋的:

  • 柯里化是指把接收多個(gè)參數(shù)的函數(shù),變成接收一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接收余下的參數(shù),而且返回結(jié)果的新函數(shù)的技術(shù);
  • 柯里化聲稱**“如果你固定某些參數(shù),你將得到接受余下參數(shù)的一個(gè)函數(shù)”**;

總結(jié):只傳遞給函數(shù)一部分參數(shù)來調(diào)用它,讓它返回一個(gè)函數(shù)去處理剩余的參數(shù)的過程就稱之為柯里化。

函數(shù)柯里化的過程

編寫一個(gè)普通的三值求和函數(shù):

function sum(x, y, z) {
 return x + y + z
}
const res = sum(10, 20, 30)
console.log(res) // 60

將以上求和函數(shù)柯里化得:

  • 將傳入的三個(gè)參數(shù)進(jìn)行拆解,依次返回一個(gè)函數(shù),并傳入一個(gè)參數(shù);
  • 在保證同樣功能的同時(shí),其調(diào)用方式卻發(fā)生了變化;
  • 注意:在拆解參數(shù)時(shí),不一定非要將參數(shù)拆成一個(gè)個(gè)的,也可以拆成2+1或1+2;
function sum(x) {
 return function(y) {
 return function(z) {
 return x + y + z
 }
 }
}
const res = sum(10)(20)(30)
console.log(res)

使用ES6箭頭函數(shù)簡寫為:

const sum = x => y => z => x + y + z

函數(shù)柯里化的特點(diǎn)及應(yīng)用

  • **讓函數(shù)的職責(zé)更加單一。**柯里化可以實(shí)現(xiàn)讓一個(gè)函數(shù)處理的問題盡可能的單一,而不是將一大堆邏輯交給一個(gè)函數(shù)來處理。

1.將上面的三值求和函數(shù)增加一個(gè)需求,在計(jì)算結(jié)果之前給每個(gè)值加上2,先看看不使用柯里化的實(shí)現(xiàn)效果:

function sum(x, y, z) {
 x = x + 2
 y = y + 2
 z = z + 2
 return x + y + z
}

2.柯里化的實(shí)現(xiàn)效果:

function sum(x) {
 x = x + 2
 return function(y) {
 y = y + 2
 return function(z) {
 z = z + 2
 return x + y + z
 }
 }
}

3.很明顯函數(shù)柯里化后,讓我們對每個(gè)參數(shù)的處理更加單一

  • **提高函數(shù)參數(shù)邏輯復(fù)用。**同樣使用上面的求和函數(shù),增加另一個(gè)需求,固定第一個(gè)參數(shù)的值為10,直接看柯里化的實(shí)現(xiàn)效果吧,后續(xù)函數(shù)調(diào)用時(shí)第一個(gè)參數(shù)值都為10的話,就可以直接調(diào)用sum10函數(shù)了。
function sum(x) {
 return function(y) {
 return function(z) {
 return x + y + z
 }
 }
}
const sum10 = sum(10) // 指定第一個(gè)參數(shù)值為10的函數(shù)
const res = sum10(20)(30)
console.log(res) // 60

自動(dòng)柯里化函數(shù)的實(shí)現(xiàn)

function autoCurrying(fn) {
 // 1.拿到當(dāng)前需要柯里化函數(shù)的參數(shù)個(gè)數(shù)
 const fnLen = fn.length

 // 2.定義一個(gè)柯里化之后的函數(shù)
 function curried\_1(...args1) {
 // 2.1拿到當(dāng)前傳入?yún)?shù)的個(gè)數(shù)
 const argsLen = args1.length

 // 2.1.將當(dāng)前傳入?yún)?shù)個(gè)數(shù)和fn需要的參數(shù)個(gè)數(shù)進(jìn)行比較
 if (argsLen >= fnLen) {
 // 如果當(dāng)前傳入的參數(shù)個(gè)數(shù)已經(jīng)大于等于fn需要的參數(shù)個(gè)數(shù)
 // 直接執(zhí)行fn,并在執(zhí)行時(shí)綁定this,并將對應(yīng)的參數(shù)數(shù)組傳入
 return fn.apply(this, args1)
 } else {
 // 如果傳入的參數(shù)不夠,說明需要繼續(xù)返回函數(shù)來接收參數(shù)
 function curried\_2(...args2) {
 // 將參數(shù)進(jìn)行合并,遞歸調(diào)用curried\_1,直到參數(shù)達(dá)到fn需要的參數(shù)個(gè)數(shù)
 return curried\_1.apply(this, [...args1, ...args2])
 }

 // 返回繼續(xù)接收參數(shù)函數(shù)
 return curried\_2
 }
 }

 // 3.將柯里化的函數(shù)返回
 return curried\_1
}

測試:

function sum(x, y, z) {
 return x + y + z
}

const curryingSum = autoCurrying(sum)

const res1 = curryingSum(10)(20)(30)
const res2 = curryingSum(10, 20)(30)
const res3 = curryingSum(10)(20, 30)
const res4 = curryingSum(10, 20, 30)
console.log(res1) // 60
console.log(res2) // 60
console.log(res3) // 60
console.log(res4) // 60

組合函數(shù)

**組合函數(shù)(Compose Function)**是在JavaScript開發(fā)過程中一種對函數(shù)的使用技巧、模式。對某一個(gè)數(shù)據(jù)進(jìn)行函數(shù)調(diào)用,執(zhí)行兩個(gè)函數(shù),這兩個(gè)函數(shù)需要依次執(zhí)行,所以需要將這兩個(gè)函數(shù)組合起來,自動(dòng)依次調(diào)用,而這個(gè)過程就叫做函數(shù)的組合,組合形成的函數(shù)就叫做組合函數(shù)。

需求:對一個(gè)數(shù)字先進(jìn)行乘法運(yùn)算,再進(jìn)行平方運(yùn)算。

  • 一般情況下,需要先定義兩個(gè)函數(shù),然后再對其依次調(diào)用:
function double(num) {
 return num * 2
}
function square(num) {
 return num ** 2
}
const duobleResult = double(10)
const squareResult = square(duobleResult)
console.log(squareResult) // 400

實(shí)現(xiàn)一個(gè)組合函數(shù),將duoble和square兩個(gè)函數(shù)組合起來:

function composeFn(fn1, fn2) {
 return function(num) {
 return fn2(fn1(num))
 }
}
const execFn = composeFn(double, square)
const res = execFn(10)
console.log(res) // 400

實(shí)現(xiàn)一個(gè)自動(dòng)組合函數(shù)的函數(shù):

function autoComposeFn(...fns) {
 // 1.拿到需要組合的函數(shù)個(gè)數(shù)
 const fnsLen = fns.length

 // 2.對傳入的函數(shù)進(jìn)行邊界判斷,所有參數(shù)必須為函數(shù)
 for (let i = 0; i < fnsLen; i++) {
 if (typeof fns[i] !== 'function') {
 throw TypeError('The argument passed must be a function.')
 }
 }

 // 3.定義一個(gè)組合之后的函數(shù)
 function composeFn(...args) {
 // 3.1.拿到第一個(gè)函數(shù)的返回值
 let result = fns[0].apply(this, args)

 // 3.1.判斷傳入的函數(shù)個(gè)數(shù)
 if (fnsLen === 1) {
 // 如果傳入的函數(shù)個(gè)數(shù)為一個(gè),直接將結(jié)果返回
 return result
 } else {
 // 如果傳入的函數(shù)個(gè)數(shù) >= 2
 // 依次將函數(shù)取出進(jìn)行調(diào)用,將上一個(gè)函數(shù)的返回值作為參數(shù)傳給下一個(gè)函數(shù)
 // 從第二個(gè)函數(shù)開始遍歷
 for (let i = 1; i < fnsLen; i++) {
 result = fns[i].call(this, result)
 }

 // 將結(jié)果返回
 return result
 }
 }

 // 4.將組合之后的函數(shù)返回
 return composeFn
}

測試:

function double(num) {
 return num * 2
}
function square(num) {
 return num ** 2 
}

const composeFn = autoComposeFn(double, square)
const res = composeFn(10)
console.log(res) // 400

到此這篇關(guān)于JS函數(shù)式編程之純函數(shù)、柯里化以及組合函數(shù)的文章就介紹到這了,更多相關(guān)純函數(shù)、柯里化、組合函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論