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

資訊專欄INFORMATION COLUMN

前端進擊的巨人(五):學會函數(shù)柯里化(curry)

chengtao1633 / 1445人閱讀

摘要:函數(shù)柯里化是把支持多個參數(shù)的函數(shù)變成接收單一參數(shù)的函數(shù),并返回一個函數(shù)能接收處理剩余參數(shù),而反柯里化就是把參數(shù)全部釋放出來。但在一些復雜的業(yè)務(wù)邏輯封裝中,函數(shù)柯里化能夠為我們提供更好的應(yīng)對方案,讓我們的函數(shù)更具自由度和靈活性。

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

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

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

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

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

函數(shù)柯里化

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

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

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

函數(shù)柯里化公式

先上幾個公式(左邊是普通函數(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í)行的機制不同:

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

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

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

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

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

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

分析代碼知識點:

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

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

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

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

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

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

// 公式類型一: 參數(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);  
        // 深拷貝閉包共用對象_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ù)量已經(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ù)不滿足原始函數(shù)參數(shù)數(shù)量時,返回curry函數(shù)
            return createCurry.call(this, fn, ..._allArgs);
        } else {
            // 調(diào)用參數(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);
        }
    }
}

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

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

先上個公式看對比:

// 函數(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ù)柯里化中,當你傳入部分參數(shù)時,返回的并不是原始函數(shù)的執(zhí)行結(jié)果,而是一個可以繼續(xù)支持后續(xù)參數(shù)的函數(shù)。而偏函數(shù)的調(diào)用方式更像是普通函數(shù)的調(diào)用方式,只調(diào)用一次,它通過原始函數(shù)內(nèi)部來實現(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ù)默認值,為沒有傳入?yún)?shù),指定默認值為0,支持無參數(shù)或不定參數(shù)傳入。

柯里化的特點:

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

延遲執(zhí)行

提前返回

柯里化的缺點

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

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

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

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

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

柯里化的應(yīng)用

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

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

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

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

一個應(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都會進行判斷

// 柯里化事件綁定函數(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)勢:判斷只執(zhí)行一次,通過閉包保留了父級作用域的判斷結(jié)果
秒懂反柯里化

先上公式,從來沒有這么喜歡寫公式,簡明易懂。

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

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

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

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

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

但在一些復雜的業(yè)務(wù)邏輯封裝中,函數(shù)柯里化能夠為我們提供更好的應(yīng)對方案,讓我們的函數(shù)更具自由度和靈活性。

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

寫在結(jié)尾

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

參考文檔:

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

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

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

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

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

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

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

相關(guān)文章

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

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

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

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

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

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

    liaorio 評論0 收藏0
  • JS中柯里

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

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

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

    edgardeng 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<