成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

高階函數(shù)應(yīng)用 —— 柯里化與反柯里化

wyk1184 / 1959人閱讀

摘要:柯里化通用式上面的柯里化函數(shù)沒(méi)涉及到高階函數(shù),也不具備通用性,無(wú)法轉(zhuǎn)換形參個(gè)數(shù)任意或未知的函數(shù),我們接下來(lái)封裝一個(gè)通用的柯里化轉(zhuǎn)換函數(shù),可以將任意函數(shù)轉(zhuǎn)換成柯里化。


閱讀原文


前言

在 JavaScript 中,柯里化和反柯里化是高階函數(shù)的一種應(yīng)用,在這之前我們應(yīng)該清楚什么是高階函數(shù),通俗的說(shuō),函數(shù)可以作為參數(shù)傳遞到函數(shù)中,這個(gè)作為參數(shù)的函數(shù)叫回調(diào)函數(shù),而擁有這個(gè)參數(shù)的函數(shù)就是高階函數(shù),回調(diào)函數(shù)在高階函數(shù)中調(diào)用并傳遞相應(yīng)的參數(shù),在高階函數(shù)執(zhí)行時(shí),由于回調(diào)函數(shù)的內(nèi)部邏輯不同,高階函數(shù)的執(zhí)行結(jié)果也不同,非常靈活,也被叫做函數(shù)式編程。


柯里化

在 JavaScript 中,函數(shù)柯里化是函數(shù)式編程的重要思想,也是高階函數(shù)中一個(gè)重要的應(yīng)用,其含義是給函數(shù)分步傳遞參數(shù),每次傳遞部分參數(shù),并返回一個(gè)更具體的函數(shù)接收剩下的參數(shù),這中間可嵌套多層這樣的接收部分參數(shù)的函數(shù),直至返回最后結(jié)果。

1、最基本的柯里化拆分
// 柯里化拆分
// 原函數(shù)
function add(a, b, c) {
    return a + b + c;
}

// 柯里化函數(shù)
function addCurrying(a) {
    return function (b) {
        return function (c) {
            return a + b + c;
        }
    }
}

// 調(diào)用原函數(shù)
add(1, 2, 3); // 6

// 調(diào)用柯里化函數(shù)
addCurrying(1)(2)(3) // 6

被柯里化的函數(shù) addCurrying 每次的返回值都為一個(gè)函數(shù),并使用下一個(gè)參數(shù)作為形參,直到三個(gè)參數(shù)都被傳入后,返回的最后一個(gè)函數(shù)內(nèi)部執(zhí)行求和操作,其實(shí)是充分的利用了閉包的特性來(lái)實(shí)現(xiàn)的。

2、柯里化通用式

上面的柯里化函數(shù)沒(méi)涉及到高階函數(shù),也不具備通用性,無(wú)法轉(zhuǎn)換形參個(gè)數(shù)任意或未知的函數(shù),我們接下來(lái)封裝一個(gè)通用的柯里化轉(zhuǎn)換函數(shù),可以將任意函數(shù)轉(zhuǎn)換成柯里化。

// 柯里化通用式 ES5
function currying(func, args) {
    // 形參個(gè)數(shù)
    var arity = func.length;
    // 上一次傳入的參數(shù)
    var args = args || [];

    return function () {
        // 將參數(shù)轉(zhuǎn)化為數(shù)組
        var _args = [].slice.call(arguments);

        // 將上次的參數(shù)與當(dāng)前參數(shù)進(jìn)行組合并修正傳參順序
        Array.prototype.unshift.apply(_args, args);

        // 如果參數(shù)不夠,返回閉包函數(shù)繼續(xù)收集參數(shù)
        if(_args.length < arity) {
            return currying.call(null, func, _args);
        }

        // 參數(shù)夠了則直接執(zhí)行被轉(zhuǎn)化的函數(shù)
        return func.apply(null, _args);
    }
}

上面主要使用的是 ES5 的語(yǔ)法來(lái)實(shí)現(xiàn),大量的使用了 callapply,下面我們通過(guò) ES6 的方式實(shí)現(xiàn)功能完全相同的柯里化轉(zhuǎn)換通用式。

// 柯里化通用式 ES6
function currying(func, args = []) {
    let arity = func.length;

    return function (..._args) {
        _args.unshift(...args);

        if(_args.length < arity) {
            return currying(func, _args);
        }

        return func(..._args);
    }
}

函數(shù) currying 算是比較高級(jí)的轉(zhuǎn)換柯里化的通用式,可以隨意拆分參數(shù),假設(shè)一個(gè)被轉(zhuǎn)換的函數(shù)有多個(gè)形參,我們可以在任意環(huán)節(jié)傳入任意個(gè)數(shù)的參數(shù)進(jìn)行拆分,舉一個(gè)例子,假如 5 個(gè)參數(shù),第一次可以傳入 2 個(gè),第二次可以傳入 1 個(gè), 第三次可以傳入剩下的,也有其他的多種傳參和拆分方案,因?yàn)樵?currying 內(nèi)部收集參數(shù)的同時(shí)按照被轉(zhuǎn)換函數(shù)的形參順序進(jìn)行了更正。

柯里化的一個(gè)很大的好處是可以幫助我們基于一個(gè)被轉(zhuǎn)換函數(shù),通過(guò)對(duì)參數(shù)的拆分實(shí)現(xiàn)不同功能的函數(shù),如下面的例子。

// 柯里化通用式應(yīng)用 —— 普通函數(shù)
// 被轉(zhuǎn)換函數(shù),用于檢測(cè)傳入的字符串是否符合正則表達(dá)式
function checkFun(reg, str) {
    return reg.test(str);
}

// 轉(zhuǎn)換柯里化
const check = currying(checkFun);

// 產(chǎn)生新的功能函數(shù)
const checkPhone = check(/^1[34578]d{9}$/);
const checkEmail = check(/^(w)+(.w+)*@(w)+((.w+)+)$/);

上面的例子根據(jù)一個(gè)被轉(zhuǎn)換的函數(shù)通過(guò)轉(zhuǎn)換變成柯里化函數(shù),并用 check 變量接收,以后每次調(diào)用 check 傳遞不同的正則就會(huì)產(chǎn)生一個(gè)檢測(cè)不同類型字符串的功能函數(shù)。

這種使用方式同樣適用于被轉(zhuǎn)換函數(shù)是高階函數(shù)的情況,比如下面的例子。

// 柯里化通用式應(yīng)用 —— 高階函數(shù)
// 被轉(zhuǎn)換函數(shù),按照傳入的回調(diào)函數(shù)對(duì)傳入的數(shù)組進(jìn)行映射
function mapFun(func, array) {
    return array.map(func);
}

// 轉(zhuǎn)換柯里化
const getNewArray = currying(mapFun);

// 產(chǎn)生新的功能函數(shù)
const createPercentArr = getNewArray(item => `${item * 100}%`);
const createDoubleArr = getNewArray(item => item * 2);

// 使用新的功能函數(shù)
let arr = [1, 2, 3, 4, 5];
let percentArr = createPercentArr(arr); // ["100%", "200%", "300%", "400%", "500%",]
let doubleArr = createDoubleArr(arr); // [2, 4, 6, 8, 10]
3、柯里化與 bind

bind 方法是經(jīng)常使用的一個(gè)方法,它的作用是幫我們將調(diào)用 bind 函數(shù)內(nèi)部的上下文對(duì)象 this 替換成我們傳遞的第一個(gè)參數(shù),并將后面其他的參數(shù)作為調(diào)用 bind 函數(shù)的參數(shù)。

// bind 方法原理模擬
// bind 方法的模擬
Function.prototype.bind = function (context) {
    var self = this;
    var args = [].slice.call(arguments, 1);

    return function () {
        return self.apply(context, args);
    }
}

通過(guò)上面代碼可以看出,其實(shí) bind 方法就是一個(gè)柯里化轉(zhuǎn)換函數(shù),將調(diào)用 bind 方法的函數(shù)進(jìn)行轉(zhuǎn)換,即通過(guò)閉包返回一個(gè)柯里化函數(shù),執(zhí)行該柯里化函數(shù)的時(shí)候,借用 apply 將調(diào)用 bind 的函數(shù)的執(zhí)行上下文轉(zhuǎn)換成了 context 并執(zhí)行,只是這個(gè)轉(zhuǎn)換函數(shù)沒(méi)有那么復(fù)雜,沒(méi)有進(jìn)行參數(shù)拆分,而是函數(shù)在調(diào)用的時(shí)候傳入了所有的參數(shù)。


反柯里化

反柯里化的思想與柯里化正好相反,如果說(shuō)柯里化的過(guò)程是將函數(shù)拆分成功能更具體化的函數(shù),那反柯里化的作用則在于擴(kuò)大函數(shù)的適用性,使本來(lái)作為特定對(duì)象所擁有的功能函數(shù)可以被任意對(duì)象所使用。

1、反柯里化通用式

反柯里化通用式的參數(shù)為一個(gè)希望可以被其他對(duì)象調(diào)用的方法或函數(shù),通過(guò)調(diào)用通用式返回一個(gè)函數(shù),這個(gè)函數(shù)的第一個(gè)參數(shù)為要執(zhí)行方法的對(duì)象,后面的參數(shù)為執(zhí)行這個(gè)方法時(shí)需要傳遞的參數(shù)。

// 反柯里化通用式 ES5
function uncurring(fn) {
    return function () {
        // 取出要執(zhí)行 fn 方法的對(duì)象,同時(shí)從 arguments 中刪除
        var obj = [].shift.call(arguments);
        return fn.apply(obj, arguments);
    }
}
// 反柯里化通用式 ES6
function uncurring(fn) {
    return function (...args) {
        return fn.call(...args);
    }
}

下面我們通過(guò)一個(gè)例子來(lái)感受一下反柯里化的應(yīng)用。

// 反柯里化通用式應(yīng)用
// 構(gòu)造函數(shù) F
function F() {}

// 拼接屬性值的方法
F.prototype.concatProps = function () {
    let args = Array.from(arguments);
    return args.reduce((prev, next) => `${this[prev]}&${this[next]}`);
}

// 使用 concatProps 的對(duì)象
let obj = {
    name: "Panda",
    age: 16
};

// 使用反柯里化進(jìn)行轉(zhuǎn)化
const concatProps = uncurring(F.prototype.concatProps);

concatProps(obj, "name", "age"); // Panda&16

反柯里化還有另外一個(gè)應(yīng)用,用來(lái)代替直接使用 callapply,比如檢測(cè)數(shù)據(jù)類型的 Object.prototype.toString 等方法,以往我們使用時(shí)是在這個(gè)方法后面直接調(diào)用 call 更改上下文并傳參,如果項(xiàng)目中多處需要對(duì)不同的數(shù)據(jù)類型進(jìn)行驗(yàn)證是很麻的,常規(guī)的解決方案是封裝成一個(gè)檢測(cè)數(shù)據(jù)類型的模塊。

// 檢測(cè)數(shù)據(jù)類型常規(guī)方案
function checkType(val) {
    return Object.prototype.toString.call(val);
}

如果需要這樣封裝的功能很多就麻煩了,代碼量也會(huì)隨之增大,其實(shí)我們也可以使用另一種解決方案,就是利用反柯里化通用式將這個(gè)函數(shù)轉(zhuǎn)換并將返回的函數(shù)用變量接收,這樣我們只需要封裝一個(gè) uncurring 通用式就可以了。

// 反柯里化創(chuàng)建檢測(cè)類型函數(shù)
const checkType = uncurring(Object.prototype.toString);

checkType(1); // [object Number]
checkType("hello"); // [object String]
checkType(true); // [object Boolean]
2、通過(guò)函數(shù)調(diào)用生成反柯里化函數(shù)

在 JavaScript 我們經(jīng)常使用面向?qū)ο蟮木幊谭绞?,在兩個(gè)類或構(gòu)造函數(shù)之間建立聯(lián)系實(shí)現(xiàn)繼承,如果我們對(duì)繼承的需求僅僅是希望一個(gè)構(gòu)造函數(shù)的實(shí)例能夠使用另一個(gè)構(gòu)造函數(shù)原型上的方法,那進(jìn)行繁瑣的繼承很浪費(fèi),簡(jiǎn)單的繼承父子類的關(guān)系又不那么的優(yōu)雅,還不如之間不存在聯(lián)系。

// 將反柯里化方法擴(kuò)展到函數(shù)原型
Function.prototype.uncurring = function () {
    var self = this;
    return function () {
        return Function.prototype.call.apply(self, arguments);
    }
}

之前的問(wèn)題通過(guò)上面給函數(shù)擴(kuò)展的 uncurring 方法完全得到了解決,比如下面的例子。

// 函數(shù)應(yīng)用反柯里化原型方法
// 構(gòu)造函數(shù)
function F() {}

F.prototype.sayHi = function () {
    return "I"m " + this.name + ", " + this.age + " years old.";
}

// 希望 sayHi 方法被任何對(duì)象使用
sayHi = F.prototype.sayHi.uncurring();

sayHi({ name: "Panda", age: 20}); // I"m Panda, 20 years old.

Function 的原型對(duì)象上擴(kuò)展的 uncurring 中,難點(diǎn)是理解 Function.prototype.call.apply,我們知道在 call 的源碼邏輯中 this 指的是調(diào)用它的函數(shù),在 call 內(nèi)部用第一個(gè)參數(shù)替換了這個(gè)函數(shù)中的 this,其余作為形參執(zhí)行了函數(shù)。

而在 Function.prototype.call.applyapply 的第一個(gè)參數(shù)更換了 call 中的 this,這個(gè)用于更換 this 的就是例子中調(diào)用 uncurring 的方法 F.prototype.sayHi,所以等同于 F.prototype.sayHi.call,arguments 內(nèi)的參數(shù)會(huì)傳入 call 中,而 arguments 的第一項(xiàng)正是用于修改 F.prototype.sayHithis 的對(duì)象。


總結(jié)

看到這里你應(yīng)該對(duì)柯里化和反柯里化有了一個(gè)初步的認(rèn)識(shí)了,但要熟練的運(yùn)用在開(kāi)發(fā)中,還需要我們更深入的去了解它們內(nèi)在的含義。


文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/98307.html

相關(guān)文章

  • 柯里簡(jiǎn)介

    摘要:與反柯里化什么是柯里化與反柯里化純函數(shù)函數(shù)結(jié)果只受傳入?yún)?shù)影響,參數(shù)一定,結(jié)果一定。寫(xiě)法柯里化接受一次性傳入多個(gè)參數(shù)調(diào)用的函數(shù),也可以傳入部分參數(shù)調(diào)用,最后使它返回一個(gè)單一參數(shù)的函數(shù)去處理,并且返回。 call與apply反柯里化? 什么是柯里化與反柯里化?純函數(shù)函數(shù)結(jié)果只受傳入?yún)?shù)影響,參數(shù)一定,結(jié)果一定。高階函數(shù)一個(gè)函數(shù)可以接收另一個(gè)函數(shù)作為參數(shù),這種函數(shù)稱為高階函數(shù)。 funct...

    songjz 評(píng)論0 收藏0
  • 函數(shù)柯里Redux中間件及applyMiddleware源碼分析

    摘要:函數(shù)的柯里化的基本使用方法和函數(shù)綁定是一樣的使用一個(gè)閉包返回一個(gè)函數(shù)。先來(lái)一段我自己實(shí)現(xiàn)的函數(shù)高程里面這么評(píng)價(jià)它們兩個(gè)的方法也實(shí)現(xiàn)了函數(shù)的柯里化。使用還是要根據(jù)是否需要對(duì)象響應(yīng)來(lái)決定。 奇怪,怎么把函數(shù)的柯里化和Redux中間件這兩個(gè)八竿子打不著的東西聯(lián)系到了一起,如果你和我有同樣疑問(wèn)的話,說(shuō)明你對(duì)Redux中間件的原理根本就不了解,我們先來(lái)講下什么是函數(shù)的柯里化?再來(lái)講下Redux的...

    jeyhan 評(píng)論0 收藏0
  • 函數(shù)式編程了解一下(上)

    摘要:一直以來(lái)沒(méi)有對(duì)函數(shù)式編程有一個(gè)全面的學(xué)習(xí)和使用,或者說(shuō)沒(méi)有一個(gè)深刻的思考。是不是輕松了其實(shí)函數(shù)式編程主張的就是以抽象的方式創(chuàng)建函數(shù)。后面咱們?cè)谙到y(tǒng)性的學(xué)習(xí)下函數(shù)式編程。 一直以來(lái)沒(méi)有對(duì)函數(shù)式編程有一個(gè)全面的學(xué)習(xí)和使用,或者說(shuō)沒(méi)有一個(gè)深刻的思考。最近看到一些博客文章,突然覺(jué)得函數(shù)式編程還是蠻有意思的??戳诵?shū)和文章。這里記載下感悟和收獲。 歡迎團(tuán)隊(duì)姜某人多多指點(diǎn)@姜少。 由于博客秉持著簡(jiǎn)...

    int64 評(píng)論0 收藏0
  • 掌握J(rèn)avaScript函數(shù)柯里

    摘要:原文鏈接和都支持函數(shù)的柯里化函數(shù)的柯里化還與的函數(shù)編程有很大的聯(lián)系如果你感興趣的話可以在這些方面多下功夫了解相信收獲一定很多看本篇文章需要知道的一些知識(shí)點(diǎn)函數(shù)部分的閉包高階函數(shù)不完全函數(shù)文章后面有對(duì)這些知識(shí)的簡(jiǎn)單解釋大家可以看看什么是柯里化 原文鏈接 Haskell和scala都支持函數(shù)的柯里化,JavaScript函數(shù)的柯里化還與JavaScript的函數(shù)編程有很大的聯(lián)系,如果你感興...

    DTeam 評(píng)論0 收藏0
  • 關(guān)于js中的柯里(Currying)與反柯里(Uncurrying)

    摘要:今天了解到一個(gè)新名詞柯里化,研究一番后總結(jié)如下一柯里化定義把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)最初函數(shù)的第一個(gè)參數(shù)的函數(shù),并且返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)的技術(shù)。如果使用反柯里化,則可以這樣寫(xiě)震驚某前端只會(huì),竟月入百萬(wàn)。。。 今天了解到一個(gè)新名詞:柯里化,研究一番后總結(jié)如下: 一· 柯里化 定義 把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并...

    Rocko 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<