最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript函數(shù)柯里化詳解

 更新時(shí)間:2022年01月14日 15:33:47   作者:天界程序員  
這篇文章主要為大家介紹了JavaScript函數(shù)柯里化,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

一、簡單了解apply和call

  • call 和 apply 都是為了改變某個(gè)函數(shù)運(yùn)行時(shí)的 context 即上下文而存在的,換句話說,就是為了改變函數(shù)體內(nèi)部 this 的指向。
  • call 和 apply二者的作用完全一樣,只是接受參數(shù)的方式不太一樣。call其實(shí)是apply的一種語法糖。
  • 格式:apply(context,[arguments]),call(context,param1,param2,...)。

二、什么是函數(shù)柯里化?

柯里化(Currying)是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)的技術(shù)。

在這里舉個(gè)例子,有一個(gè)add()函數(shù),它是用來處理我們傳給它的參數(shù)(param1,params2,…)相加求和的一個(gè)函數(shù)。

// 在這里第一個(gè)具有兩個(gè)參數(shù)`x`、`y`的`add(x , y)`函數(shù)
function add(x , y){
	return x + y;
}
// 調(diào)用`add()`函數(shù),并給定兩個(gè)參數(shù)`4`和`6`
add(4,6);
// 模擬計(jì)算機(jī)操作,第一步 傳入第一個(gè)參數(shù) 4
function add(4 , y){
	return 4 + y;
}
// 模擬計(jì)算機(jī)操作,第二步 傳入第一個(gè)參數(shù) 6
function add(4 , 6){
	return 4 + 6;
}

如果我們將add()函數(shù)柯里化,是什么樣子呢?在這里簡單的實(shí)現(xiàn)一下:

// 柯里化過的add()函數(shù),可以接受部分參數(shù)
function add(x ,y){
	if (typeof y === 'undefined') {
		return function (newy){
			return x + newy;
		}
	}
	// 完整應(yīng)用
	return x + y;
}
// 測試調(diào)用
console.log(typeof add(4)); // [Function]
console.log(add(4)(6)); // 10
// 可以創(chuàng)建保存函數(shù)
let saveAdd = add(4);
console.log(saveAdd(6)); // 10

從以上簡單柯里化的add()函數(shù)可以看出,函數(shù)可以接受部分函數(shù),然后返回一個(gè)新的函數(shù),使其繼續(xù)處理剩下的函數(shù)。

三、寫一個(gè)公共的柯里化函數(shù)

在這里我們創(chuàng)建一個(gè)公共的柯里化函數(shù),那樣我們就不必每次寫一個(gè)函數(shù)都要在其內(nèi)部實(shí)現(xiàn)復(fù)雜的柯里化過程。

// 定義一個(gè)createCurry的函數(shù)
function createCurry(fn){
	var slice = Array.prototype.slice,
	stored_args = slice.call(arguments,1);
	return function () {
		let new_args = slice.call(arguments),
		args = stored_args.concat(new_args);
		return fn.apply(null,args);
	}
}

在以上公共的柯里化函數(shù)中:

  • arguments,并不是一個(gè)真的數(shù)組,只是一個(gè)具有length屬性的對(duì)象,所以我們從Array.prototype中借用slice方法幫我們把arguments轉(zhuǎn)為一個(gè)真正的數(shù)組,方便我們更好的操作。
  • 當(dāng)我們第一次調(diào)用函數(shù)createCurry的時(shí)候,其中變量stored_args 是保持了除去第一個(gè)參數(shù)以外的參數(shù),因?yàn)榈谝粋€(gè)參數(shù)是我們需要柯里化的函數(shù)。
  • 當(dāng)我們執(zhí)行createCurry函數(shù)中返回的函數(shù)時(shí),變量new_args獲取參數(shù)并轉(zhuǎn)為數(shù)組。
  • 內(nèi)部返回的函數(shù)通過閉包訪問變量stored_args中存儲(chǔ)的值和變量new_args的值合并為一個(gè)新的數(shù)組,并賦值給變量args
  • 最后調(diào)用fn.apply(null,args)方法,執(zhí)行被柯里化的函數(shù)。

現(xiàn)在我們來測試公共的柯里化函數(shù)

// 普通函數(shù)add()
function add(x , y){
	return x + y;
}
// 柯里化得到一個(gè)新的函數(shù)
var newAdd = createCurry(add,4);
console.log(newAdd(6)); // 10

//另一種簡便方式
console.log(createCurry(add,4)(6));// 10

當(dāng)然這里并不局限于兩個(gè)參數(shù)的柯里化,也可以多個(gè)參數(shù):

// 多個(gè)參數(shù)的普通函數(shù)
function add(a,b,c,d){
	return a + b + c + d;
}
// 柯里化函數(shù)得到新函數(shù),多個(gè)參數(shù)可以隨意分割
console.log(createCurry(add,4,5)(5,6)); // 20
// 兩步柯里化
let add_one = createCurry(add,5);
console.log(add_one(5,5,5));// 20
let add_two = createCurry(add_one,4,6);
console.log(add_two(6)); // 21

通過以上的例子,我們可以發(fā)現(xiàn)一個(gè)局限,那就是不管是兩個(gè)參數(shù)還是多個(gè)參數(shù),它只能分兩步執(zhí)行,如以下公式:

  • fn(x,y) ==> fn(x)(y);
  • fn(x,y,z,w) ==> fn(x)(y,z,w) || fn(x,y)(z,w)||…

如果我們想更靈活一點(diǎn):

  • fn(x,y) ==> fn(x)(y);
  • fn(x,y,z) ==> fn(x,y)(z) || fn(x)(y)(z);
  • fn(x,y,z,w) ==> fn(x,y)(z)(w) || fn(x)(y)(z)(w) || …;

我們?cè)撛趺磳?shí)現(xiàn)呢?

四、創(chuàng)建一個(gè)靈活的柯里化函數(shù)

經(jīng)過以上練習(xí),我們發(fā)現(xiàn)我們創(chuàng)建的柯里化函數(shù)存在一定局限性,我們希望函數(shù)可以分為多步執(zhí)行:

// 創(chuàng)建一個(gè)可以多步執(zhí)行的柯里化函數(shù),當(dāng)參數(shù)滿足數(shù)量時(shí)就去執(zhí)行它:
// 函數(shù)公式:fn(x,y,z,w) ==> fn(x)(y)(z)(w);
let createCurry = (fn,...params)=> {
	let args = parsms || [];
	let fnLen = fn.length; // 指定柯里化函數(shù)的參數(shù)長度
	return (...res)=> {
		// 通過作用域鏈獲取上一次的所有參數(shù)
		let allArgs = args.slice(0);
		// 深度拷貝閉包共用的args參數(shù),避免后續(xù)操作影響(引用類型)
		allArgs.push(...res);
		if(allArgs.length < fnLen){
		   // 當(dāng)參數(shù)數(shù)量小于原函數(shù)的參數(shù)長度時(shí),遞歸調(diào)用createCurry函數(shù)
		   return createCurry.call(this,fn,...allArgs);
		}else{
		  // 當(dāng)參數(shù)數(shù)量滿足時(shí),觸發(fā)函數(shù)執(zhí)行
		  return fn.apply(this,allArgs);
		}
	}
}

// 多個(gè)參數(shù)的普通函數(shù)
function add(a,b,c,d){
	return a + b + c + d;
}
// 測試柯里化函數(shù)
let curryAdd = createCurry(add,1);
console.log(curryAdd(2)(3)(4)); // 10

以上我們已經(jīng)實(shí)現(xiàn)了靈活的柯里化函數(shù),但是這里我們又發(fā)現(xiàn)了一個(gè)問題:

  • 如果我第一次就把參數(shù)全部傳入,但是它并沒有返回結(jié)果,而是一個(gè)函數(shù)(function)。
  • 只有我們?cè)俅螌⒎祷氐暮瘮?shù)調(diào)用一次才能返回結(jié)果:curryAdd(add,1,2,3,4)();
  • 可能有人說如果是全部傳參,就調(diào)用原來的add()函數(shù)就行了,這也是一種辦法;但是我們?cè)谶@里既然是滿足參數(shù)數(shù)量,對(duì)于這種情況我們還是處理一下。

在這里我們只需要在返回函數(shù)前做一下判斷就行了:

let createCurry = (fn,...params)=> {
	let args = parsms || [];
	let fnLen = fn.length; // 指定柯里化函數(shù)的參數(shù)長度
	if(length === _args.length){
	   // 加入判斷,如果第一次參數(shù)數(shù)量以經(jīng)足夠時(shí)就直接調(diào)用函數(shù)獲取結(jié)果
           return fn.apply(this,args);
        }
	return (...res)=> {
		let allArgs = args.slice(0);
		allArgs.push(...res);
		if(allArgs.length < fnLen){
		   return createCurry.call(this,fn,...allArgs);
		}else{
		  return fn.apply(this,allArgs);
		}
	}
}

以上可以算是完成了一個(gè)靈活的柯里化的函數(shù)了,但是這里還不算很靈活,因?yàn)槲覀儾荒芸刂扑裁磿r(shí)候執(zhí)行,只要參數(shù)數(shù)量足夠它就自動(dòng)執(zhí)行。我們希望實(shí)現(xiàn)一個(gè)可以控制它執(zhí)行的時(shí)機(jī)該怎么辦呢?

五、寫一個(gè)可控制的執(zhí)行時(shí)間的柯里化函數(shù)

我們這里直接說明一下函數(shù)公式:

  • fn(a,b,c) ==> fn(a)(b)(c )();
  • fn(a,b,c) ==> fn(a);fn(b);fn(c );fn();
  • 當(dāng)我們參數(shù)足夠時(shí)它并不會(huì)執(zhí)行,只有我們?cè)俅握{(diào)用一次函數(shù)它才會(huì)執(zhí)行并返回結(jié)果。在這里我們?cè)谝陨侠又屑右粋€(gè)小小的條件就可以實(shí)現(xiàn)。
// 當(dāng)參數(shù)滿足,再次執(zhí)行時(shí)調(diào)用函數(shù)
let createCurry = (fn,...params)=> {
	let args = parsms || [];
	let fnLen = fn.length; // 指定柯里化函數(shù)的參數(shù)長度
	//當(dāng)然這里的判斷需要注釋掉,不然當(dāng)它第一次參數(shù)數(shù)量足夠時(shí)就直接執(zhí)行結(jié)果了
	//if(length === _args.length){
	   // 加入判斷,如果第一次參數(shù)數(shù)量以經(jīng)足夠時(shí)就直接調(diào)用函數(shù)獲取結(jié)果
           //return fn.apply(this,args);
        //}
	return (...res)=> {
		let allArgs = args.slice(0);
		allArgs.push(...res);
		// 在這里判斷輸入的參數(shù)是否大于0,如果大于0在判斷參數(shù)數(shù)量是否足夠,
		// 這里不能用 && ,如果用&& 也是參數(shù)數(shù)量足夠時(shí)就執(zhí)行結(jié)果了。
		if(res.length > 0 || allArgs.length < fnLen){
		   return createCurry.call(this,fn,...allArgs);
		}else{
		  return fn.apply(this,allArgs);
		}
	}
}

// 多個(gè)參數(shù)的普通函數(shù)
function add(a,b,c,d){
	return a + b + c + d;
}
// 測試可控制的柯里化函數(shù)
let curryAdd = createCurry(add,1);
console.log(curryAdd(2)(3)(4)); // function
console.log(curryAdd(2)(3)(4)()); // 10
console.log(curryAdd(2)(3)()); // 當(dāng)參數(shù)不足夠時(shí)返回 NaN

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • Javascript的promise,async和await的區(qū)別詳解

    Javascript的promise,async和await的區(qū)別詳解

    這篇文章主要為大家詳細(xì)介紹了Javascript的promise,async和await的區(qū)別,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • 淺析JavaScript定時(shí)器setTimeout的時(shí)延問題

    淺析JavaScript定時(shí)器setTimeout的時(shí)延問題

    這篇文章主要為大家詳細(xì)介紹了JavaScript中定時(shí)器setTimeout有最小時(shí)延的相關(guān)知識(shí),文中的示例代碼簡潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-11-11
  • JS實(shí)現(xiàn)點(diǎn)擊掉落特效

    JS實(shí)現(xiàn)點(diǎn)擊掉落特效

    這篇文章主要介紹了JS實(shí)現(xiàn)點(diǎn)擊掉落特效,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • Javascript中的for in循環(huán)和hasOwnProperty結(jié)合使用

    Javascript中的for in循環(huán)和hasOwnProperty結(jié)合使用

    當(dāng)檢測某個(gè)對(duì)象是否擁有某個(gè)屬性時(shí),hasOwnProperty 是唯一可以完成這一任務(wù)的方法,在 for in 循環(huán)時(shí),建議增加 hasOwnProperty 進(jìn)行判斷,可以有效避免擴(kuò)展本地原型而引起的錯(cuò)誤
    2013-06-06
  • JavaScript如何實(shí)現(xiàn)LRU緩存淘汰算法

    JavaScript如何實(shí)現(xiàn)LRU緩存淘汰算法

    LRU(Least Recently Used)緩存淘汰算法是一種常見的緩存淘汰策略,它的核心思想是優(yōu)先淘汰最近最少使用的緩存數(shù)據(jù),以保證緩存中的數(shù)據(jù)始終是最熱門的。本文主要介紹了一些關(guān)于如何實(shí)現(xiàn)LRU緩存淘汰算法的方法,感興趣的小伙伴可以參考一下
    2023-04-04
  • javascript encodeURI和encodeURIComponent的比較

    javascript encodeURI和encodeURIComponent的比較

    在進(jìn)行SaaS前端開發(fā)的時(shí)候,大家經(jīng)常會(huì)用到兩個(gè)JavaScriptNative函數(shù):encodeURI 和 encodeURIComponent。這篇文章詳細(xì)解釋這兩個(gè)函數(shù)的用途并比較它們的不同之處
    2010-04-04
  • js幾個(gè)不錯(cuò)的函數(shù) $$()

    js幾個(gè)不錯(cuò)的函數(shù) $$()

    js幾個(gè)不錯(cuò)的函數(shù) $$()...
    2006-10-10
  • showModalDialog模態(tài)對(duì)話框的使用詳解以及瀏覽器兼容

    showModalDialog模態(tài)對(duì)話框的使用詳解以及瀏覽器兼容

    showModalDialog是jswindow對(duì)象的一個(gè)方法,和window.open一樣都是打開一個(gè)新的頁面。區(qū)別是:showModalDialog打開子窗口后,父窗口就不能獲取焦點(diǎn)了(也就是無法操作了)
    2014-01-01
  • Bootstrap網(wǎng)頁布局網(wǎng)格的實(shí)現(xiàn)

    Bootstrap網(wǎng)頁布局網(wǎng)格的實(shí)現(xiàn)

    柵格就是網(wǎng)格,本文詳細(xì)的介紹了Bootstrap網(wǎng)頁布局網(wǎng)格的原理和實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 基于JavaScript怎么實(shí)現(xiàn)讓歌詞滾動(dòng)播放

    基于JavaScript怎么實(shí)現(xiàn)讓歌詞滾動(dòng)播放

    各種音樂播放器上都有一個(gè)自動(dòng)滾動(dòng)播放歌詞的功能,當(dāng)前滾動(dòng)到的歌詞會(huì)高亮居中顯示,即使歌詞被換行也能正常居中,那么這個(gè)功能基于JavaScript怎么實(shí)現(xiàn)讓歌詞滾動(dòng)播放呢?感興趣的朋友一起看看吧
    2015-11-11

最新評(píng)論

青岛市| 若羌县| 花垣县| 平乡县| 太仓市| 永春县| 上高县| 普宁市| 庄浪县| 万安县| 宜州市| 谷城县| 虹口区| 十堰市| 鄂尔多斯市| 东港市| 定西市| 泾阳县| 云龙县| 五河县| 浦北县| 花莲县| 阳高县| 闽清县| 扬州市| 高密市| 浦东新区| 云浮市| 扶风县| 康乐县| 泾源县| 克山县| 达拉特旗| 高淳县| 荔波县| 葫芦岛市| 奉节县| 阿克苏市| 大理市| 黄陵县| 新化县|