国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

前端進(jìn)擊的巨人(五):學(xué)會(huì)函數(shù)柯里化(curry)

chengtao1633 / 1365人閱讀

摘要:函數(shù)柯里化是把支持多個(gè)參數(shù)的函數(shù)變成接收單一參數(shù)的函數(shù),并返回一個(gè)函數(shù)能接收處理剩余參數(shù),而反柯里化就是把參數(shù)全部釋放出來。但在一些復(fù)雜的業(yè)務(wù)邏輯封裝中,函數(shù)柯里化能夠?yàn)槲覀兲峁└玫膽?yīng)對(duì)方案,讓我們的函數(shù)更具自由度和靈活性。

柯里化(Curring, 以邏輯學(xué)家Haskell Curry命名)
寫在開頭

柯里化理解的基礎(chǔ)來源于我們前幾篇文章構(gòu)建的知識(shí),如果還未能掌握閉包,建議回閱前文。

代碼例子會(huì)用到 apply/call ,一般用來實(shí)現(xiàn)對(duì)象冒充,例如字符串冒充數(shù)組對(duì)象,讓字符串擁有數(shù)組的方法。待對(duì)象講解篇會(huì)細(xì)分解析。在此先了解,兩者功能相同,區(qū)別在于參數(shù)傳遞方式的不同, apply 參數(shù)以數(shù)組方式傳遞,call 多個(gè)參數(shù)則是逗號(hào)隔開。

apply(context, [arguments]);
call(context, arg1, arg2, arg3, ....);

代碼例子中使用到了ES6語法,對(duì)ES6還不熟悉的話,可學(xué)習(xí)社區(qū)這篇文章:《30分鐘掌握ES6/ES2015核心內(nèi)容(上)》

函數(shù)柯里化

函數(shù)柯里化在JavaScript中其實(shí)是高階函數(shù)的一種應(yīng)用,上篇文章我們簡(jiǎn)略介紹了高階函數(shù)(可以作為參數(shù)傳遞,或作為返回值)。

理論知識(shí)太枯燥,來個(gè)生活小例子,"存款買房"(富二代繞道)。假設(shè)買房是我們存錢的終極目標(biāo)。那么在買房前,存在卡里的錢(老婆本)就不能動(dòng)。等到夠錢買房了,錢從銀行卡取出來,開始買買買。。。

函數(shù)柯里化就像我們往卡里存錢,存夠了,才能執(zhí)行買房操作,存不夠,接著存。

函數(shù)柯里化公式

先上幾個(gè)公式(左邊是普通函數(shù),右邊就是轉(zhuǎn)化后柯里化函數(shù)支持的調(diào)用方式):

// 公式類型一
fn(a,b,c,d) => fn(a)(b)(c)(d);
fn(a,b,c,d) => fn(a, b)(c)(d);
fn(a,b,c,d) => fn(a)(b,c,d);

// 公式類型二
fn(a,b,c,d) => fn(a)(b)(c)(d)();
fn(a,b,c,d) => fn(a);fn(b);fn(c);fn(d);fn();

兩種公式類型的區(qū)別 —— 函數(shù)觸發(fā)執(zhí)行的機(jī)制不同:

公式一當(dāng)傳入?yún)?shù)等于函數(shù)參數(shù)數(shù)量時(shí)開始執(zhí)行

公式二當(dāng)沒有參數(shù)傳入時(shí)(且參數(shù)數(shù)量滿足)開始執(zhí)行

通過公式,我們先來理解這行代碼 fn(a)(b)(c)(d), 執(zhí)行 fn(a) 時(shí)返回的是一個(gè)函數(shù),并且支持傳參。何時(shí)返回目標(biāo)函數(shù)結(jié)果值而不是函數(shù)的觸發(fā)機(jī)制,控制權(quán)在我們手里,我們可以為函數(shù)制定不同的觸發(fā)機(jī)制。

普通的函數(shù)調(diào)用,一次性傳入?yún)?shù)就執(zhí)行。而通過柯里化,它可以幫我們實(shí)現(xiàn)函數(shù)部分參數(shù)傳入執(zhí)行(并未立即執(zhí)行原始函數(shù),錢沒存夠接著存),這就是函數(shù)柯里化的特點(diǎn):"延遲執(zhí)行和部分求值"

"函數(shù)柯里化:指封裝一個(gè)函數(shù),接收原始函數(shù)作為參數(shù)傳入,并返回一個(gè)能夠接收并處理剩余參數(shù)的函數(shù)"

函數(shù)柯里化的例子
// 等待我們柯里化實(shí)現(xiàn)的方法add
function add(a, b, c, d) {
    return a + b + c + d;
};
// 最簡(jiǎn)單地實(shí)現(xiàn)函數(shù)add的柯里化
// 有點(diǎn)low,有助于理解
function add(a, b, c, d) {
    return function(a) {
        return function(b) {
            return function(c) {
                return a + b + c + d;
            }
        }
    }
}

分析代碼知識(shí)點(diǎn):

函數(shù)作為返回值返回,閉包形成,外部環(huán)境可訪問函數(shù)內(nèi)部作用域

子函數(shù)可訪問父函數(shù)的作用域,作用域由內(nèi)而外的作用域鏈查找規(guī)則,作用域嵌套形成

在函數(shù)參數(shù)數(shù)量不滿足時(shí),返回一個(gè)函數(shù)(該函數(shù)可接收并處理剩余參數(shù))

當(dāng)函數(shù)數(shù)量滿足我們的觸發(fā)機(jī)制(可自由制定),觸發(fā)原始函數(shù)執(zhí)行

前幾篇文章的知識(shí)點(diǎn)此時(shí)剛好??梢娀A(chǔ)知識(shí)的重要性,高階的東西始終要靠小磚頭堆砌出來。

弄清原理后,接下來就是將代碼寫得更通用些(高大上些)。

// 公式類型一: 參數(shù)數(shù)量滿足函數(shù)參數(shù)要求,觸發(fā)執(zhí)行
// fn(a,b,c,d) => fn(a)(b)(c)(d);

const createCurry = (fn, ...args) => {
    let _args = args || [];
    let length = fn.length; // fn.length代碼函數(shù)參數(shù)數(shù)量

    return (...rest) => {
        let _allArgs = _args.slice(0);  
        // 深拷貝閉包共用對(duì)象_args,避免后續(xù)操作影響(引用類型)
        _allArgs.push(...rest);
        if (_allArgs.length < length) {
            // 參數(shù)數(shù)量不滿足原始函數(shù)數(shù)量,返回curry函數(shù)
            return createCurry.call(this, fn, ..._allArgs);
        } else {
            // 參數(shù)數(shù)量滿足原始函數(shù)數(shù)量,觸發(fā)執(zhí)行
            return fn.apply(this, _allArgs);
        }
    }
}

const curryAdd = createCurry(add, 2);
let sum = curryAdd(3)(4)(5);    // 14

// ES5寫法
function createCurry() {
    var fn = arguments[0];
    var _args = [].slice.call(arguments, 1);
    var length = fn.length;
    
    return function() {
        var _allArgs = _args.slice(0);
        _allArgs = _allArgs.concat([].slice.call(arguments));
        if (_allArgs.length < length) {
            _allArgs.unshift(fn);
            return createCurry.apply(this, _allArgs);
        } else {
            return fn.apply(this, _allArgs);
        }
    }
}
// 公式類型二: 無參數(shù)傳入時(shí)并且參數(shù)數(shù)量已經(jīng)滿足函數(shù)要求
// fn(a, b, c, d) => fn(a)(b)(c)(d)();
// fn(a, b, c, d) => fn(a);fn(b);fn(c);fn(d);fn();

const createCurry = (fn, ...args) => {
    let all = args || [];
    let length = fn.length;

    return (...rest) => {
        let _allArgs = all.slice(0);
        _allArgs.push(...rest);
        if (rest.length > 0 || _allArgs.length < length) {
            // 調(diào)用時(shí)參數(shù)不為空或存儲(chǔ)的參數(shù)不滿足原始函數(shù)參數(shù)數(shù)量時(shí),返回curry函數(shù)
            return createCurry.call(this, fn, ..._allArgs);
        } else {
            // 調(diào)用參數(shù)為空(),且參數(shù)數(shù)量滿足時(shí),觸發(fā)執(zhí)行
            return fn.apply(this, _allArgs);
        }
    }
}
const curryAdd = createCurry(add, 2);
let sum = curryAdd(3)(4)(5)();  // 14

// ES5寫法
function createCurry() {
    var fn = arguments[0];
    var _args = [].slice.call(arguments, 1);
    var length = fn.length;
    
    return function() {
        var _allArgs = _args.slice(0);
        _allArgs = _allArgs.concat([].slice.call(arguments));
        if (arguments.length > 0 || _allArgs.length < length) {
            _allArgs.unshift(fn);
            return createCurry.apply(this, _allArgs);
        } else {
            return fn.apply(this, _allArgs);
        }
    }
}

為實(shí)現(xiàn)公式中不同的兩種調(diào)用公式,兩個(gè)createCurry方法制定了兩種不同的觸發(fā)機(jī)制。記住一個(gè)點(diǎn),函數(shù)觸發(fā)機(jī)制可根據(jù)需求自行制定。

偏函數(shù)與柯里化的區(qū)別

先上個(gè)公式看對(duì)比:

// 函數(shù)柯里化:參數(shù)數(shù)量完整
fn(a,b,c,d) => fn(a)(b)(c)(d);
fn(a,b,c,d) => fn(a,b)(c)(d);

// 偏函數(shù):只執(zhí)行了部分參數(shù)
fn(a,b,c,d) => fn(a);
fn(a,b,c,d) => fn(a, b);

"函數(shù)柯里化中,當(dāng)你傳入部分參數(shù)時(shí),返回的并不是原始函數(shù)的執(zhí)行結(jié)果,而是一個(gè)可以繼續(xù)支持后續(xù)參數(shù)的函數(shù)。而偏函數(shù)的調(diào)用方式更像是普通函數(shù)的調(diào)用方式,只調(diào)用一次,它通過原始函數(shù)內(nèi)部來實(shí)現(xiàn)不定參數(shù)的支持。"

如果已經(jīng)看懂上述柯里化的代碼例子,那么改寫支持偏函數(shù)的代碼,并不難。

// 公式:
// fn(a, b, c, d) => fn(a);
// fn(a, b, c, d) => fn(a,b,c);

const partialAdd = (a = 0, b = 0, c = 0, d = 0) => {
    return a + b + c +d;
}

partialAdd(6);      // 6
partialAdd(2, 3);   // 5

使用ES6函數(shù)參數(shù)默認(rèn)值,為沒有傳入?yún)?shù),指定默認(rèn)值為0,支持無參數(shù)或不定參數(shù)傳入。

柯里化的特點(diǎn):

參數(shù)復(fù)用(固定易變因素)

延遲執(zhí)行

提前返回

柯里化的缺點(diǎn)

柯里化是犧牲了部分性能來實(shí)現(xiàn)的,可能帶來的性能損耗:

存取 arguments 對(duì)象要比存取命名參數(shù)要慢一些

老版本瀏覽器在 arguments.lengths 的實(shí)現(xiàn)相當(dāng)慢(新版本瀏覽器忽略)

fn.apply()fn.call() 要比直接調(diào)用 fn()

大量嵌套的作用域和閉包會(huì)帶來開銷,影響內(nèi)存占用和作用域鏈查找速度

柯里化的應(yīng)用

利用柯里化制定約束條件,管控觸發(fā)機(jī)制

處理瀏覽器兼容(參數(shù)復(fù)用實(shí)現(xiàn)一次性判斷)

函數(shù)節(jié)流防抖(延遲執(zhí)行)

ES5前bind方法的實(shí)現(xiàn)

一個(gè)應(yīng)用例子:瀏覽器事件綁定的兼容處理
// 普通事件綁定函數(shù)
var addEvent = function(ele, type, fn, isCapture) {
    if(window.addEventListener) {
        ele.addEventListener(type, fn, isCapture)
    } else if(window.attachEvent) {

        ele.attachEvent("on" + type, fn)
    }
}
// 弊端:每次調(diào)用addEvent都會(huì)進(jìn)行判斷

// 柯里化事件綁定函數(shù)
var addEvent = (function() {
    if(window.addEventListener) {
        return function(ele, type, fn, isCapture) {
            ele.addEventListener(type, fn, isCapture)
        }
    } else if(window.attachEvent) {
        return function(ele, type, fn) {
             ele.attachEvent("on" + type, fn)
        }
    }
})()
// 優(yōu)勢(shì):判斷只執(zhí)行一次,通過閉包保留了父級(jí)作用域的判斷結(jié)果
秒懂反柯里化

先上公式,從來沒有這么喜歡寫公式,簡(jiǎn)明易懂。

// 反柯里化公式:
curryFn(a)(b)(c)(d) = fn(a, b, c, d);
curryFn(a) = fn(a);

看完公式,是不是似曾相識(shí),這不就是我們?nèi)粘G么a的普通函數(shù)么?沒錯(cuò)的,函數(shù)柯里化就是把普通函數(shù)變成成一個(gè)復(fù)雜的函數(shù),而反柯里化其就是柯里化的逆反,把復(fù)雜變得簡(jiǎn)單。

函數(shù)柯里化是把支持多個(gè)參數(shù)的函數(shù)變成接收單一參數(shù)的函數(shù),并返回一個(gè)函數(shù)能接收處理剩余參數(shù):fn(a,b,c,d) => fn(a)(b)(c)(d),而反柯里化就是把參數(shù)全部釋放出來:fn(a)(b)(c)(d) => fn(a,b,c,d)。

// 反柯里化:最簡(jiǎn)單的反柯里化(普通函數(shù))
function add(a, b, c, d) {
    return a + b + c + d;
}
反思:為何要使用柯里化

函數(shù)柯里化是函數(shù)編程中的一個(gè)重要的基礎(chǔ),它為我們提供了一種編程的思維方式。顯然,它讓我們的函數(shù)處理變得復(fù)雜,代碼調(diào)用方式并不直觀,還加入了閉包,多層作用域嵌套,會(huì)有一些性能上的影響。

但在一些復(fù)雜的業(yè)務(wù)邏輯封裝中,函數(shù)柯里化能夠?yàn)槲覀兲峁└玫膽?yīng)對(duì)方案,讓我們的函數(shù)更具自由度和靈活性。

實(shí)際開發(fā)中,如果你的邏輯處理相對(duì)復(fù)雜,不妨換個(gè)思維,用函數(shù)柯里化來實(shí)現(xiàn),技能包不嫌多。
說到底,程序員就是解決問題的那群人。

寫在結(jié)尾

本篇函數(shù)柯里化知識(shí)點(diǎn)的理解確實(shí)存在難度,暫時(shí)跳過這章也無妨,可以先了解再深入。耐得主寂寞的小伙伴回頭多啃幾遍,沒準(zhǔn)春季面試就遇到了。

參考文檔:

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

前端基礎(chǔ)進(jìn)階(八):深入詳解函數(shù)的柯里化

系列更文請(qǐng)關(guān)注專欄:《前端進(jìn)擊的巨人》,不斷更新中。。。

本文首發(fā)Github,期待Star!
https://github.com/ZengLingYong/blog

作者:以樂之名
本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。轉(zhuǎn)載請(qǐng)指明出處。

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

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

相關(guān)文章

  • 前端進(jìn)擊巨人(六):知否知否,須知this

    摘要:有關(guān)函數(shù)柯里化的詳解,請(qǐng)回閱前端進(jìn)擊的巨人五學(xué)會(huì)函數(shù)柯里化。構(gòu)造函數(shù)中的通過操作符可以實(shí)現(xiàn)對(duì)函數(shù)的構(gòu)造調(diào)用。在了解構(gòu)造函數(shù)中的前,有必要先了解下實(shí)例化對(duì)象的過程。 showImg(https://segmentfault.com/img/bVburMp?w=800&h=600); 常見this的誤解 指向函數(shù)自身(源于this英文意思的誤解) 指向函數(shù)的詞法作用域(部分情況) th...

    Andrman 評(píng)論0 收藏0
  • JavaScript函數(shù)式編程(一)

    摘要:所以下面介紹一些函數(shù)式編程的知識(shí)和概念。函數(shù)式編程的一個(gè)明顯的好處就是這種聲明式的代碼,對(duì)于無副作用的純函數(shù),我們完全可以不考慮函數(shù)內(nèi)部是如何實(shí)現(xiàn)的,專注于編寫業(yè)務(wù)代碼。我會(huì)在下一篇文章中介紹函數(shù)式編程的更加高階一些的知識(shí),例如等等概念。 一、引言 說到函數(shù)式編程,大家可能第一印象都是學(xué)院派的那些晦澀難懂的代碼,充滿了一大堆抽象的不知所云的符號(hào),似乎只有大學(xué)里的計(jì)算機(jī)教授才會(huì)使用這些東...

    Shihira 評(píng)論0 收藏0
  • 前端面試題系列6」理解函數(shù)柯里

    摘要:原題如下寫一個(gè)方法,當(dāng)使用下面的語法調(diào)用時(shí),能正常工作這道題要考察的,就是對(duì)函數(shù)柯里化的理解。當(dāng)參數(shù)只有一個(gè)的時(shí)候,進(jìn)行柯里化的處理。這其實(shí)就是函數(shù)柯里化的簡(jiǎn)單應(yīng)用。 showImg(https://segmentfault.com/img/bVbopGm?w=620&h=350); 前言 這是前端面試題系列的第 6 篇,你可能錯(cuò)過了前面的篇章,可以在這里找到: ES6 中箭頭函數(shù)的...

    liaorio 評(píng)論0 收藏0
  • JS中柯里

    摘要:作為函數(shù)式編程語言,帶來了很多語言上的有趣特性,比如柯里化和反柯里化。個(gè)人理解不知道對(duì)不對(duì)延遲執(zhí)行柯里化的另一個(gè)應(yīng)用場(chǎng)景是延遲執(zhí)行。不斷的柯里化,累積傳入的參數(shù),最后執(zhí)行。 作為函數(shù)式編程語言,JS帶來了很多語言上的有趣特性,比如柯里化和反柯里化。 這里可以對(duì)照另外一篇介紹 JS 反柯里化 的文章一起看~ 1. 簡(jiǎn)介 柯里化(Currying),又稱部分求值(Partial Evalu...

    Hancock_Xu 評(píng)論0 收藏0
  • JavaScript 函數(shù)式編程技巧 - 柯里

    摘要:作為函數(shù)式編程語言,帶來了很多語言上的有趣特性,比如柯里化和反柯里化。在一些函數(shù)式編程語言中,會(huì)定義一個(gè)特殊的占位變量。個(gè)人理解不知道對(duì)不對(duì)延遲執(zhí)行柯里化的另一個(gè)應(yīng)用場(chǎng)景是延遲執(zhí)行。不斷的柯里化,累積傳入的參數(shù),最后執(zhí)行。作為函數(shù)式編程語言,JS帶來了很多語言上的有趣特性,比如柯里化和反柯里化。 這里可以對(duì)照另外一篇介紹 JS 反柯里化 的文章一起看~ 1. 簡(jiǎn)介 柯里化(Currying)...

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

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

0條評(píng)論

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