成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

高級(jí)函數(shù)技巧-函數(shù)柯里化

shixinzhang / 2561人閱讀

摘要:如果你對(duì)函數(shù)式編程有一定了解,函數(shù)柯里化是不可或缺的,利用函數(shù)柯里化,可以在開發(fā)中非常優(yōu)雅的處理復(fù)雜邏輯。同樣先看簡(jiǎn)單版本的方法,以方法為例,代碼來自高級(jí)程序設(shè)計(jì)加強(qiáng)版實(shí)現(xiàn)上面函數(shù),可以換成任何其他函數(shù),經(jīng)過函數(shù)處理,都可以轉(zhuǎn)成柯里化函數(shù)。

我們經(jīng)常說在Javascript語言中,函數(shù)是“一等公民”,它們本質(zhì)上是十分簡(jiǎn)單和過程化的??梢岳煤瘮?shù),進(jìn)行一些簡(jiǎn)單的數(shù)據(jù)處理,return 結(jié)果,或者有一些額外的功能,需要通過使用閉包來實(shí)現(xiàn),最后經(jīng)常會(huì)return 匿名函數(shù)。

如果你對(duì)函數(shù)式編程有一定了解,函數(shù)柯里化(function currying)是不可或缺的,利用函數(shù)柯里化,可以在開發(fā)中非常優(yōu)雅的處理復(fù)雜邏輯。

函數(shù)柯里化

柯里化(Currying),維基百科上的解釋是,把接受多個(gè)參數(shù)的函數(shù)轉(zhuǎn)換成接受一個(gè)單一參數(shù)的函數(shù)
先看一個(gè)簡(jiǎn)單例子

    // 柯里化
    var foo = function(x) {
        return function(y) {
            return x + y
        }
    }
    
    foo(3)(4)       // 7

    
    // 普通方法
    var add = function(x, y) {
        return x + y;
    }
    
    add(3, 4)       //7 

本來應(yīng)該一次傳入兩個(gè)參數(shù)的add函數(shù),柯里化方法,變成每次調(diào)用都只用傳入一個(gè)參數(shù),調(diào)用兩次后,得到最后的結(jié)果。

再看看,一道經(jīng)典的面試題。

編寫一個(gè)sum函數(shù),實(shí)現(xiàn)如下功能:
 console.log(sum(1)(2)(3)) // 6.

直接套用上面柯里化函數(shù),多加一層return

   function sum(a) {
        return function(b) {
            return function(c) {
                return a + b + c;
            }
        }
    }

當(dāng)然,柯里化不是為了解決面試題,它是應(yīng)函數(shù)式編程而生,

如何實(shí)現(xiàn)

還是看看上面的經(jīng)典面試題。
如果想實(shí)現(xiàn) sum(1)(2)(3)(4)(5)...(n)就得嵌套n-1個(gè)匿名函數(shù),

   function sum(a) {
        return function(b) {
             ...
            return function(n) {
                
            }
        }
    }
    

看起來并不優(yōu)雅,如果我們預(yù)先知道有多少個(gè)參數(shù)要傳入,可以利用遞歸方法解決

    var add = function(num1, num2) {
        return num1 + num2;
    }
    
    // 假設(shè) sum 函數(shù)調(diào)用時(shí),傳入?yún)?shù)都是標(biāo)準(zhǔn)的數(shù)字
    function curry(add, n) {
       var count = 0,
           arr = [];
           
       return function reply(arg) {
           arr.push(arg);
           
           if ( ++count >= n) {
               //這里也可以在外面定義變量,保存每次計(jì)算后結(jié)果
               return arr.reduce(function(p, c) {
                   return p = add(p, c);
               }, 0) 
           } else {
               return reply;
           }
       }
    }
    var sum = curry(add, 4);
    
    sum(4)(3)(2)(1)  // 10

如果調(diào)用次數(shù)多于約定數(shù)量,sum 就會(huì)報(bào)錯(cuò),我們就可以設(shè)計(jì)成類似這樣

sum(1)(2)(3)(4)(); // 最后傳入空參數(shù),標(biāo)識(shí)調(diào)用結(jié)束,

只需要簡(jiǎn)單修改下curry 函數(shù)

function curry(add) {
       var arr = [];
       
       return function reply() {
         var arg = Array.prototype.slice.call(arguments);
         arr = arr.concat(arg);
         
          if (arg.length === 0) { // 遞歸結(jié)束條件,修改為 傳入空參數(shù)
              return arr.reduce(function(p, c) {
                  return p = add(p, c);
              }, 0)
          } else {
              return reply;
          }
      }
    }
  
  console.log(sum(4)(3)(2)(1)(5)())   // 15
簡(jiǎn)潔版實(shí)現(xiàn)

上面針對(duì)具體問題,引入柯里化方法解答,回到如何實(shí)現(xiàn)創(chuàng)建柯里化函數(shù)的通用方法。
同樣先看簡(jiǎn)單版本的方法,以add方法為例,代碼來自《JavaScript高級(jí)程序設(shè)計(jì)》

 function curry(fn) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function() {
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(null, finalArgs);
    };
}

function add(num1, num2) {
    return num1 + num2;
}
var curriedAdd = curry(add, 5);

var curriedAdd2 = curry(add, 5, 12);

alert(curriedAdd(3))    // 8
alert(curriedAdd2())    // 17
加強(qiáng)版實(shí)現(xiàn)

上面add函數(shù),可以換成任何其他函數(shù),經(jīng)過curry函數(shù)處理,都可以轉(zhuǎn)成柯里化函數(shù)。
這里在調(diào)用curry初始化時(shí),就傳入了一個(gè)參數(shù),而且返回的函數(shù) curriedAdd , curriedAdd2也沒有被柯里化。要想實(shí)現(xiàn)更加通用的方法,在柯里化函數(shù)真正調(diào)用時(shí),再傳參數(shù),

function curry(fn) {
     ...
 }

function add(num1, num2) {
    return num1 + num2;
}

var curriedAdd = curry(add);

curriedAdd(3)(4) // 7

每次調(diào)用curry返回的函數(shù),也被柯里化,可以繼續(xù)傳入一個(gè)或多個(gè)參數(shù)進(jìn)行調(diào)用,

跟上面sum(1)(2)(3)(4) 非常類似,利用遞歸就可以實(shí)現(xiàn)。 關(guān)鍵是遞歸的出口,這里不能是傳入一個(gè)空參數(shù)的調(diào)用, 而是原函數(shù)定義時(shí),參數(shù)的總個(gè)數(shù),柯里化函數(shù)調(diào)用時(shí),滿足了原函數(shù)的總個(gè)數(shù),就返回計(jì)算結(jié)果,否則,繼續(xù)返回柯里化函數(shù)。

原函數(shù)的入?yún)⒖倐€(gè)數(shù),可以利用length 屬性獲得

function add(num1, num2) {
    return num1 + num2;
}

add.length // 2

結(jié)合上面的代碼,

    var curry = function(f) {
      var len = f.length;
      
        return function t() {
          var innerLength = arguments.length,
            args = Array.prototype.slice.call(arguments);
            
          if (innerLength >= len) {   // 遞歸出口,f.length
             return f.apply(undefined, args)
          } else {
            return function() {
              var innerArgs = Array.prototype.slice.call(arguments),
                allArgs = args.concat(innerArgs);
                
              return t.apply(undefined, allArgs)
            }
          }
        }
    }
    
   // 測(cè)試一下
  function add(num1, num2) {
    return num1 + num2;
  }

   var curriedAdd = curry(add);
   add(2)(3);     //5

  // 一個(gè)參數(shù)
  function identity(value) {
     return value;
 }

   var curriedIdentify = curry(identify);
   curriedIdentify(4) // 4

到此,柯里化通用函數(shù)可以滿足大部分需求了。

在使用 apply 遞歸調(diào)用的時(shí)候,默認(rèn)傳入 undefined, 在其它場(chǎng)景下,可能需要傳入 context, 綁定指定環(huán)境

實(shí)際開發(fā),推薦使用 lodash.curry , 具體實(shí)現(xiàn),可以參考下curry源碼

使用場(chǎng)景

講了這么多curry函數(shù)的不同實(shí)現(xiàn)方法,那么實(shí)現(xiàn)了通用方法后,在那些場(chǎng)景下可以使用,或者說使用柯里化函數(shù)是否可以真實(shí)的提高代碼質(zhì)量,下面總結(jié)一下使用場(chǎng)景

參數(shù)復(fù)用
在《JavaScript高級(jí)程序設(shè)計(jì)》中簡(jiǎn)單版的curry函數(shù)中

  var curriedAdd = curry(add, 5)

在后面,使用curriedAdd函數(shù)時(shí),默認(rèn)都復(fù)用了5,不需要重新傳入兩個(gè)參數(shù)

延遲執(zhí)行
上面?zhèn)魅攵鄠€(gè)參數(shù)的sum(1)(2)(3),就是延遲執(zhí)行的最后例子,傳入?yún)?shù)個(gè)數(shù)沒有滿足原函數(shù)入?yún)€(gè)數(shù),都不會(huì)立即返回結(jié)果。

類似的場(chǎng)景,還有綁定事件回調(diào),更使用bind()方法綁定上下文,傳入?yún)?shù)類似,

   addEventListener("click", hander.bind(this, arg1,arg2...))
   
   addEventListener("click", curry(hander)) 
   

延遲執(zhí)行的特性,可以避免在執(zhí)行函數(shù)外面,包裹一層匿名函數(shù),curry函數(shù)作為回調(diào)函數(shù)就有很大優(yōu)勢(shì)。

函數(shù)式編程中,作為compose, functor, monad 等實(shí)現(xiàn)的基礎(chǔ)

有人說柯里化是應(yīng)函數(shù)式編程而生,它在里面出現(xiàn)的概率就非常大了,在JS 函數(shù)式編程指南中,開篇就介紹了柯里化的重要性。

關(guān)于額外開銷

函數(shù)柯里化可以用來構(gòu)建復(fù)雜的算法 和 功能, 但是濫用也會(huì)帶來額外的開銷。

從上面實(shí)現(xiàn)部分的代碼中,可以看到,使用柯里化函數(shù),離不開閉包, arguments, 遞歸。

閉包,函數(shù)中的變量都保存在內(nèi)存中,內(nèi)存消耗大,有可能導(dǎo)致內(nèi)存泄漏。
遞歸,效率非常差,
arguments, 變量存取慢,訪問性很差,

參考鏈接

JS 函數(shù)式編程指南

邂逅函數(shù)柯里化

掌握J(rèn)avaScript函數(shù)的柯里化

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/102033.html

相關(guān)文章

  • JavaScript 高級(jí)技巧——“高級(jí)函數(shù)”的注意要點(diǎn)

    摘要:語法如下注意這里使用的并不是的,是內(nèi)部函數(shù)的。函數(shù)柯里化的基本方法是使用一個(gè)閉包返回一個(gè)函數(shù)。當(dāng)函數(shù)被調(diào)用時(shí),返回的函數(shù)還需要設(shè)置一些傳入的參數(shù)。 安全的類型檢測(cè) typeof操作符 檢測(cè)數(shù)據(jù)類型的結(jié)果可能會(huì)不正確; instanceof操作符 操作符在多個(gè)全局作用域下存在問題: var value = []; var isArray = value instanceof Array;...

    solocoder 評(píng)論0 收藏0
  • JS程序設(shè)計(jì)高級(jí)技巧

    摘要:關(guān)于定時(shí)器要記住的最重要的事情是指定的時(shí)間間隔表示何時(shí)將定時(shí)器的代碼添加到隊(duì)列,而不是何時(shí)實(shí)際執(zhí)行代碼。多個(gè)定時(shí)器之間的執(zhí)行間隔會(huì)比預(yù)期的小解決辦法處理中數(shù)組分塊,,函數(shù)節(jié)流,實(shí)際進(jìn)行處理的方法實(shí)際執(zhí)行的代碼初始處理調(diào)用的方法 一、高級(jí)函數(shù) 安全類型檢測(cè) Object.protitype.toString.call(value) 作用域安全的構(gòu)造函數(shù) function Pers...

    Codeing_ls 評(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
  • js 閉包的使用技巧

    摘要:閉包的學(xué)術(shù)定義先來參考下各大權(quán)威對(duì)閉包的學(xué)術(shù)定義百科閉包,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。 前言 上一章講解了閉包的底層實(shí)現(xiàn)細(xì)節(jié),我想大家對(duì)閉包的概念應(yīng)該也有了個(gè)大概印象,但是真要用簡(jiǎn)短的幾句話來說清楚,這還真不是件容易的事。這里我們就來總結(jié)提煉下閉包的概念,以應(yīng)付那些非專人士的心血來潮。 閉包的學(xué)術(shù)...

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

    摘要:作為函數(shù)式編程語言,帶來了很多語言上的有趣特性,比如柯里化和反柯里化。而反柯里化,從字面講,意義和用法跟函數(shù)柯里化相比正好相反,擴(kuò)大適用范圍,創(chuàng)建一個(gè)應(yīng)用范圍更廣的函數(shù)。作為函數(shù)式編程語言,JS帶來了很多語言上的有趣特性,比如柯里化和反柯里化。 可以對(duì)照另外一篇介紹 JS 柯里化 的文章一起看~ 1. 簡(jiǎn)介 柯里化,是固定部分參數(shù),返回一個(gè)接受剩余參數(shù)的函數(shù),也稱為部分計(jì)算函數(shù),目的是為了縮...

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

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

0條評(píng)論

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