真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

javascript實(shí)現(xiàn)函數(shù)柯里化與反柯里化過程解析

函數(shù)柯里化(黑人問號(hào)臉)???Currying(黑人問號(hào)臉)???妥妥的中式翻譯既視感;下面來一起看看究竟什么是函數(shù)柯里化:

成都創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都做網(wǎng)站、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的神木網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

維基百科的解釋是:把接收多個(gè)參數(shù)的函數(shù)變換成接收一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并返回接受剩余的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。其由數(shù)學(xué)家Haskell Brooks Curry提出,并以curry命名。

概念往往都是干澀且難懂的,讓我們用人話來解釋就是:如果我們不確定這個(gè)函數(shù)有多少個(gè)參數(shù),我們可以先給它傳入一個(gè)參數(shù),然后通過JS閉包(如若不懂JS閉包,請(qǐng)先學(xué)習(xí)閉包知識(shí)點(diǎn)再來學(xué)習(xí)本篇博文https://www.jb51.net/article/171398.htm)來進(jìn)行返回一個(gè)函數(shù),內(nèi)部函數(shù)接收除開第一個(gè)參數(shù)外的其余參數(shù)進(jìn)行操作并輸出,這個(gè)就是函數(shù)的柯里化;

舉個(gè)小例子:

場(chǎng)景(需求):

眾所周知程序員每天加班的時(shí)間還是比較多的,如果我們需要計(jì)算一個(gè)程序員每天的加班時(shí)間,那么我們的第一反應(yīng)應(yīng)該是這樣;

var overtime=0;
function time(x){
  return overtime+=x;
}

time(1); //1
time(2); //3
time(3); //6

上面的代碼固然沒有問題,可是需要每天調(diào)用都算加一下當(dāng)天的時(shí)間,很麻煩,并且每調(diào)用一次函數(shù)都要進(jìn)行一定的操作,如果數(shù)據(jù)量巨大,有可能會(huì)有影響性能的風(fēng)險(xiǎn),那么有沒有可以偷懶又能解決問題的辦法呢?有的!

function time(x){
 return function(y){
    return x+y;
  }   
}

var times=time(0);
times(3);

但是上面代碼依然存在問題,在實(shí)際開發(fā)中很多時(shí)候我們的參數(shù)是不確定的,上面代碼雖然簡(jiǎn)單的實(shí)現(xiàn)了柯里化的基本操作,但是對(duì)于參數(shù)不確定的情況是處理不了的;所以存在著函數(shù)參數(shù)的局限性;不過我們從上面的代碼中基本可以知道函數(shù)柯里化是個(gè)啥意思了;就是一個(gè)函數(shù)調(diào)用的時(shí)候只允許傳入一個(gè)參數(shù),然后通過閉包返回內(nèi)部函數(shù)去處理和接收剩余參數(shù),返回的函數(shù)通過閉包的方式記住了time的第一個(gè)參數(shù);

我們?cè)賮戆汛a改造一下:

// 首先定義一個(gè)變量接收函數(shù)
var overtime = (function() {
//定義一個(gè)數(shù)組用來接收參數(shù)
 var args = [];
//這里運(yùn)用閉包,調(diào)用外部函數(shù)返回一個(gè)內(nèi)部函數(shù)
 return function() {
  //arguments是瀏覽器內(nèi)置對(duì)象,專門用來接收參數(shù)
  //如果參數(shù)的長度為0即沒有參數(shù)的時(shí)候
  if(arguments.length === 0) {
    //定義變量用來累加
   var time = 0;
    //循環(huán)累加,用i和args的長度進(jìn)行比較
   for (var i = 0, l = args.length; i < l; i++) {
    //進(jìn)行累加操作  等價(jià)于time=time+args[i]
    time += args[i];
   }
    // 返回累加的結(jié)果
   return time;
    //如果arguments對(duì)象參數(shù)長度不為零,即有參數(shù)的時(shí)候
  }else {
    //定義的空數(shù)組添加arguments參數(shù)作為數(shù)組項(xiàng),第一個(gè)參數(shù)古args作為改變this指向,第二個(gè)參數(shù)arguments把剩余參數(shù)作為數(shù)組形式添加至空數(shù)組中
   [].push.apply(args, arguments);
  }
 }
})();

overtime(3.5);  // 第一天
overtime(4.5);  // 第二天
overtime(2.1);  // 第三天
//...

console.log( overtime() );  // 10.1

代碼經(jīng)過我們的改造已經(jīng)實(shí)現(xiàn)了功能,但是這不是一個(gè)函數(shù)柯里化的完整實(shí)現(xiàn),那么我們要怎么完整實(shí)現(xiàn)呢?下面我們來介紹一種通用的實(shí)現(xiàn)方式:

通用的實(shí)現(xiàn)方式:

//定義方法currying,先傳入一個(gè)參數(shù)
var currying=function(fn){
  //定義空數(shù)組裝arguments對(duì)象的剩余參數(shù)
 var args=[];
  //利用閉包返回一個(gè)函數(shù)處理剩余參數(shù)
 return function (){
    //如果arguments的參數(shù)長度為0,即沒有剩余參數(shù)
  if(arguments.length===0){
    //執(zhí)行上面方法
    //這里的this指向下面的s,類似于s(),代表參數(shù)長度為0的時(shí)候直接調(diào)用函數(shù)
   return fn.apply(this,args)
  }
  console.log(arguments)
  //如果arguments的參數(shù)長度不為0,即還有剩余參數(shù)
  //在數(shù)組的原型對(duì)象上添加數(shù)組,apply用來更改this的指向?yàn)閍rgs
  //將[].slice.call(arguments)的數(shù)組添加到原型數(shù)組上
  //這里的[].slice.call(arguments)===Array.prototype.slice.call(arguments)實(shí)質(zhì)上就是將arguments對(duì)象轉(zhuǎn)成數(shù)組并具有slice功能

  Array.prototype.push.apply(args,[].slice.call(arguments))
  //args.push([].slice.call(arguments))
  console.log(args)
  //這里返回的arguments.callee是返回的閉包函數(shù),callee是arguments對(duì)象里面的一個(gè)屬性,用于返回正被執(zhí)行的function對(duì)象
  return arguments.callee
 }
}
  //這里調(diào)用currying方法并傳入add函數(shù),結(jié)果會(huì)返回閉包內(nèi)部函數(shù)
 var s=currying(add);
  //調(diào)用閉包內(nèi)部函數(shù),當(dāng)有參數(shù)的時(shí)候會(huì)將參數(shù)逐步添加到args數(shù)組中,待沒有參數(shù)傳入的時(shí)候直接調(diào)用
  //調(diào)用的時(shí)候支持鏈?zhǔn)讲僮? s(1)(2)(3)();
//也可以一次性傳入多個(gè)參數(shù)
  s(1,2,3);
 console.log(s());

JS函數(shù)柯里化的優(yōu)點(diǎn):

1.可以延遲計(jì)算,即如果調(diào)用柯里化函數(shù)傳入?yún)?shù)是不調(diào)用的,會(huì)將參數(shù)添加到數(shù)組中存儲(chǔ),等到?jīng)]有參數(shù)傳入的時(shí)候進(jìn)行調(diào)用;

2.參數(shù)復(fù)用,當(dāng)在多次調(diào)用同一個(gè)函數(shù),并且傳遞的參數(shù)絕大多數(shù)是相同的,那么該函數(shù)可能是一個(gè)很好的柯里化候選。

世間萬物相對(duì),有因必有果,當(dāng)然了,有柯里化必然有反柯里化;

反柯里化(uncurrying)

從字面意思上來講就是跟柯里化的意思相反;其實(shí)真正的反柯里化的作用是擴(kuò)大適用范圍,就是說當(dāng)我們調(diào)用某個(gè)方法的時(shí)候,不需要考慮這個(gè)對(duì)象自身在設(shè)計(jì)的過程中有沒有這個(gè)方法,只要這個(gè)方法適用于它,我們就可以使用;(這里引用的是動(dòng)態(tài)語言中的鴨子類型的思想)

在學(xué)習(xí)JS反柯里化之前,我們先學(xué)習(xí)一下動(dòng)態(tài)語言的鴨子類型思想,以助于我們更好的理解:

動(dòng)態(tài)語言鴨子類型思想(維基百科解釋):

在程序設(shè)計(jì)中,鴨子類型(duck typing)是動(dòng)態(tài)類型的一種風(fēng)格。

在這種風(fēng)格中,一個(gè)對(duì)象有效的語義,不是由繼承自特定的類或?qū)崿F(xiàn)特定的接口,而是由當(dāng)前方法和屬性的集合決定。

這個(gè)概念的名字來源于由 James Whitcomb Riley 提出的鴨子測(cè)試,“鴨子測(cè)試”可以這樣表述:

當(dāng)看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子。

理論上的解釋往往干澀難懂,換成人話來說就是:你是你媽媽的兒子/女兒,不管你是否優(yōu)秀,是否漂亮,只要你是你媽親生的,那么你就是你媽的兒子/女兒;換成鴨子類型就是,只要你會(huì)呱呱叫,走起來像鴨子,只要你擁有的行為像鴨子,不管你是不是鴨子,那么你就可以被稱為鴨子;

在Javascript中有很多鴨子類型的引用,比如我們?cè)趯?duì)一個(gè)變量進(jìn)行賦值的時(shí)候,顯然是不需要考慮變量的類型的,正是因?yàn)槿绱耍琂avascript才更加的靈活,所以Javascript是一門典型的動(dòng)態(tài)類型語言;

我們來看一下反柯里化中是怎么引用鴨子類型的:

//函數(shù)原型對(duì)象上添加uncurring方法
Function.prototype.uncurring = function() {
//改變this的指向  
//這里的this指向是Array.prototype.push
 var self = this;
  //這里的閉包用來返回內(nèi)部函數(shù)的執(zhí)行
 return function() {
  //創(chuàng)建一個(gè)變量,在數(shù)組的原型對(duì)象上添加shift上面刪除第一個(gè)參數(shù)
  //改變數(shù)組this的指向?yàn)閍rguments
  var obj = Array.prototype.shift.call(arguments);
  //最后返回執(zhí)行并給方法改變指向?yàn)閛bj也就是arguments
  // 并傳入arguments作為參數(shù)
  return self.apply(obj, arguments);
 };
};

//數(shù)組原型對(duì)象上添加uncurrying方法
var push = Array.prototype.push.uncurring();

//測(cè)試一下
//匿名函數(shù)自執(zhí)行
(function() {
  //這里的push就是一個(gè)函數(shù)方法了
  //相當(dāng)于傳入?yún)?shù)arguments和4兩個(gè)參數(shù),但是在上面shift方法中刪除第一個(gè)參數(shù),這里的arguments參數(shù)被截取了,所以最后實(shí)際上只傳入了4
 push(arguments, 4);
 console.log(arguments); //[1, 2, 3, 4]
//匿名函數(shù)自調(diào)用并帶入?yún)?shù)1,2,3
})(1, 2, 3)

到這里大家可以想一想arguments是一個(gè)接收參數(shù)的對(duì)象,里面是沒有push方法的,那么arguments為什么能調(diào)用push方法呢?

這是因?yàn)榇avar push = Array.prototype.push.uncurring();在數(shù)組的原型對(duì)象的push方法上添加了uncurring方法,然后在執(zhí)行匿名函數(shù)的方法push(arguments, 4);時(shí)候?qū)嵸|(zhì)上是在調(diào)用上面的方法在Function的原型對(duì)象上添加uncurring方法并返回一個(gè)閉包內(nèi)部函數(shù)執(zhí)行,在執(zhí)行的過程中因?yàn)锳rray原型對(duì)象上的shift方法會(huì)把 push(arguments, 4);中的arguments截取,所以其實(shí)方法的實(shí)際調(diào)用是push(4),所以最終的結(jié)果才是[1,2,3,4]

在《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》一書中,JS函數(shù)的反柯里化的案例是這樣寫的:

//定義一個(gè)對(duì)象
var obj = {
  "length":1,
  "0":1
}
//在Function原型對(duì)象定義方法uncurrying
Function.prototype.uncurrying = function() {
  //this指向Array.prototype.push
  var self = this;
  //閉包返回一個(gè)內(nèi)部函數(shù)
  return function() {
  // 這里可以拆開理解
  //首先執(zhí)行apply return 
  //Function.prototype.call(Array.prototype.push[obj,2])
  //然后Array.prototype.push.call(obj,2)
  //call改變指向 obj.push(2)
  //所以最后結(jié)果就是 {0: 1, 1: 2, length: 2}
    return Function.prototype.call.apply(self, arguments);
}
}

//在
var push = Array.prototype.push.uncurrying()

push(obj, 2) 
console.log(obj);
//{0: 1, 1: 2, length: 2}

上面的方式不好理解?沒關(guān)系,咱們來個(gè)好理解的:

Function.prototype.unCurrying = function () {
  var self = this;
  return function () {
    //[].slice.call(arguments,1)===Array.prototype.push.slice.call(arguments,1)===arguments.slice(1)
return self.apply(arguments[0], [].slice.call(arguments, 1));
  };
};
var push = Array.prototype.push.uncurrying()
console.log(push);
push(obj,2) //{0: 1, 1: 2, length: 2}
console.log(obj);

分析一下:

1、首先在Function原型對(duì)象上添加uncurrying方法,這樣所有的Function都可以借用;

2、返回一個(gè)閉包內(nèi)部函數(shù)

3、閉包函數(shù)返回的結(jié)果中返回的是調(diào)用方法,self指向Array.prototype.push,apply方法中第一個(gè)參數(shù)是更改指向,對(duì)應(yīng)下面push(obj,2)相當(dāng)于更改指向?yàn)閛bj.push(2)

4、apply方法中第二個(gè)參數(shù)的call方法是更改指向?yàn)閍rguments,并且arguments中能使用slice方法,等于arguments.slice(1)

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。


分享名稱:javascript實(shí)現(xiàn)函數(shù)柯里化與反柯里化過程解析
轉(zhuǎn)載注明:http://weahome.cn/article/jcodpg.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部