摘要:柯里化函數的作用函數柯里化允許和鼓勵你分隔復雜功能變成更小更容易分析的部分。指向的是調用方法的一個函數,綁定,延遲執行可見,之后返回的是一個延遲執行的新函數關于性能的備注通常,使用柯里化會有一些開銷。
引子
有這樣一道題目,實現一個函數,實現如下功能:
var result = sum(1)(2)(3); console.log(result);//6
這道題目,印象中是一道技術筆試題。結合查到的資料,在這里做一下簡單的分析和總結。
一個簡單的例子題目給的還是比較寬的,沒多少限制,給了很多自由發揮的空間。
下面我們就一步一步的去實現,一種簡單的做法可以是這樣的:
function add(a){ var sum = 0; sum += a; return function(b){ sum += b; return function(c){ sum += c; return sum; } } } add(1)(2)(3);//6
嗯,沒什么問題。
在此基礎上,我們再進一步:
如果對調用的次數不加限制,比如 四次,那上面的代碼就不行了。 那該怎么辦呢?
觀察一下,我們可以發現返回的每一個函數執行的邏輯其實都是一樣的。
就此我們可以精簡下代碼,讓函數返回后返回自身。
來試一下:
function add(a){ var sum = 0; sum += a; return function temp(b) { sum += b; return temp; } } add(2)(3)(4)(5); 輸出的結果: //function temp(b) { // sum += b; // return temp; // }
并沒有像我們預期的那樣輸出 14,其實是這樣的,每次函數調用后返回的就是一個函數對象,那最后的結果,肯定是一個字符串表示啊。
要修正的話,有兩個辦法。
判斷參數,當沒有輸入參數時,返回調用結果:
function add(a){ var sum = 0; sum += a; return function temp(b) { if (arguments.length === 0) { return sum; } else { sum= sum+ b; return temp; } } } add(2)(3)(4)(5)(); //14
如果要使用匿名函數,也可以:
function add() { var _args = []; return function(){ if(arguments.length === 0) { return _args.reduce(function(a,b) { return a + b; }); } [].push.apply(_args, [].slice.call(arguments)); return arguments.callee; } } var sum = add(); sum(2,3)(4)(5)(); //14
2 . 利用JS中對象到原始值的轉換規則。
當一個對象轉換成原始值時,先查看對象是否有valueOf方法。
如果有并且返回值是一個原始值,那么直接返回這個值。
如果沒有valueOf 或 返回的不是原始值,那么調用toString方法,返回字符串表示。
那我們就為函數對象添加一個valueOf方法 和 toString方法:
function add(a) { var sum = 0; sum += a; var temp = function(b) { if(arguments.length===0){ return sum; } else { sum = sum+ b; return temp; } } temp.toString = temp.valueOf = function() { return sum; } return temp; } add(2)(3)(4)(5); //14
寫到這里,我們來簡單總結下。
柯里化的定義柯里化通常也稱部分求值,其含義是給函數分步傳遞參數,每次傳遞參數后,部分應用參數,并返回一個更具體的函數接受剩下的參數,中間可嵌套多層這樣的接受部分參數函數,逐步縮小函數的適用范圍,逐步求解,直至返回最后結果。一個通用的柯里化函數
var curring = function(fn){ var _args = []; return function cb(){ if(arguments.length === 0) { return fn.apply(this, _args); } Array.prototype.push.apply(_args, [].slice.call(arguments)); return cb; } } var multi = function(){ var total = 0; var argsArray = Array.prototype.slice.call(arguments); argsArray.forEach(function(item){ total += item; }) return total }; var calc = curring(multi); calc(1,2)(3)(4,5,6); console.log(calc()); //空白調用時才真正計算
這樣 calc = currying(multi),調用非常清晰.
如果要 累加多個值,可以把多個值作為做個參數 calc(1,2,3),也可以支持鏈式的調用,如 calc(1)(2)(3);
到這里, 不難看出,柯里化函數具有以下特點:
函數可以作為參數傳遞
函數能夠作為函數的返回值
閉包
說了這么多,柯里化函數到底能夠幫我做什么,或者說,我為什么要用柯里化函數呢? 我們接著往下看。
柯里化函數的作用函數柯里化允許和鼓勵你分隔復雜功能變成更小更容易分析的部分。這些小的邏輯單元顯然是更容易理解和測試的,然后你的應用就會變成干凈而整潔的組合,由一些小單元組成的組合。
1.提高通用性
function square(i) { return i * i; } function double(i) { return i *= 2; } function map(handeler, list) { return list.map(handeler); } // 數組的每一項平方 map(square, [1, 2, 3, 4, 5]); map(square, [6, 7, 8, 9, 10]); map(square, [10, 20, 30, 40, 50]); // ...... // 數組的每一項加倍 map(double, [1, 2, 3, 4, 5]); map(double, [6, 7, 8, 9, 10]); map(double, [10, 20, 30, 40, 50]);
例子中,創建了一個map通用函數,用于適應不同的應用場景。顯然,通用性不用懷疑。同時,例子中重復傳入了相同的處理函數:square和dubble。
應用中這種可能會更多。當然,通用性的增強必然帶來適用性的減弱。但是,我們依然可以在中間找到一種平衡。
看下面,我們利用柯里化改造一下:
function currying(fn) { var slice = Array.prototype.slice, __args = slice.call(arguments, 1); return function () { var __inargs = slice.call(arguments); return fn.apply(null, __args.concat(__inargs)); }; } function square(i) { return i * i; } function double(i) { return i *= 2; } function map(handeler, list) { return list.map(handeler); } var mapSQ = currying(map, square); mapSQ([1, 2, 3, 4, 5]); //[1, 4, 9, 16, 25] var mapDB = currying(map, double); mapDB([1, 2, 3, 4, 5]); //[2, 4, 6, 8, 10]
我們縮小了函數的適用范圍,但同時提高函數的適性.
2 延遲執行。
柯里化的另一個應用場景是延遲執行。不斷的柯里化,累積傳入的參數,最后執行。
3.固定易變因素。
柯里化特性決定了它這應用場景。提前把易變因素,傳參固定下來,生成一個更明確的應用函數。最典型的代表應用,是bind函數用以固定this這個易變對象。
Function.prototype.bind = function(ctx) { var fn = this; return function() { fn.apply(ctx, arguments); }; };
Function.prototype.bind 方法也是柯里化應用與 call/apply 方法直接執行不同,bind 方法 將第一個參數設置為函數執行的上下文,其他參數依次傳遞給調用方法(函數的主體本身不執行,可以看成是延遲執行),并動態創建返回一個新的函數, 這符合柯里化特點。
var foo = { x: 666 }; var bar = function () { console.log(this.x); }.bind(foo); // 綁定 bar(); //666 // 下面是一個 bind 函數的模擬,testBind 創建并返回新的函數,在新的函數中將真正要執行業務的函數綁定到實參傳入的上下文,延遲執行了。 Function.prototype.testBind = function (scope) { var self = this; // this 指向的是調用 testBind 方法的一個函數, return function () { return self.apply(scope); } }; var testBindBar = bar.testBind(foo); // 綁定 foo,延遲執行 console.log(testBindBar); // Function (可見,bind之后返回的是一個延遲執行的新函數) testBindBar(); // 666關于curry性能的備注
通常,使用柯里化會有一些開銷。取決于你正在做的是什么,可能會或不會,以明顯的方式影響你。也就是說,幾乎大多數情況,你的代碼的擁有性能瓶頸首先來自其他原因,而不是這個。
有關性能,這里有一些事情必須牢記于心:
存取arguments對象通常要比存取命名參數要慢一點.
一些老版本的瀏覽器在arguments.length的實現上是相當慢的.
創建大量嵌套作用域和閉包函數會帶來花銷,無論是在內存還是速度上.
以上 ;)
參考資料http://blog.jobbole.com/77956/
http://www.cnblogs.com/pigtai...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81424.html
摘要:如果你對函數式編程有一定了解,函數柯里化是不可或缺的,利用函數柯里化,可以在開發中非常優雅的處理復雜邏輯。同樣先看簡單版本的方法,以方法為例,代碼來自高級程序設計加強版實現上面函數,可以換成任何其他函數,經過函數處理,都可以轉成柯里化函數。 我們經常說在Javascript語言中,函數是一等公民,它們本質上是十分簡單和過程化的。可以利用函數,進行一些簡單的數據處理,return 結果,...
摘要:如果有一方是布爾值,則轉換為,轉換為,再進行判斷。等同運算符類型不同返回類型相同如果同為數字字符串則比較值如果同為布爾值,相同則為不同為如果兩個操作數同為引用類型,且引用的為同一個對象函數,數組,則相同。 本文主要記錄平時開發遇到的知識點和小技巧 相等判斷(==) 類型相同: 判斷其值是否相同 類型不同: 1. 如果數字和字符串比較, 則字符串會被隱式轉換為數字,在做判斷。 2....
摘要:柯里化通用式上面的柯里化函數沒涉及到高階函數,也不具備通用性,無法轉換形參個數任意或未知的函數,我們接下來封裝一個通用的柯里化轉換函數,可以將任意函數轉換成柯里化。 showImg(https://segmentfault.com/img/remote/1460000018998373); 閱讀原文 前言 在 JavaScript 中,柯里化和反柯里化是高階函數的一種應用,在這之前...
摘要:函數柯里化是把支持多個參數的函數變成接收單一參數的函數,并返回一個函數能接收處理剩余參數,而反柯里化就是把參數全部釋放出來。但在一些復雜的業務邏輯封裝中,函數柯里化能夠為我們提供更好的應對方案,讓我們的函數更具自由度和靈活性。 showImg(https://segmentfault.com/img/bVburN1?w=800&h=600); 柯里化(Curring, 以邏輯學家Has...
摘要:作為函數式編程語言,帶來了很多語言上的有趣特性,比如柯里化和反柯里化。在一些函數式編程語言中,會定義一個特殊的占位變量。個人理解不知道對不對延遲執行柯里化的另一個應用場景是延遲執行。不斷的柯里化,累積傳入的參數,最后執行。作為函數式編程語言,JS帶來了很多語言上的有趣特性,比如柯里化和反柯里化。 這里可以對照另外一篇介紹 JS 反柯里化 的文章一起看~ 1. 簡介 柯里化(Currying)...
閱讀 8995·2021-11-18 10:02
閱讀 2584·2019-08-30 15:43
閱讀 2659·2019-08-30 13:50
閱讀 1373·2019-08-30 11:20
閱讀 2708·2019-08-29 15:03
閱讀 3631·2019-08-29 12:36
閱讀 930·2019-08-23 17:04
閱讀 619·2019-08-23 14:18