裸奔向前 發表於 2019-9-10 10:09:00

简单粗暴详细讲解javascript实现函数柯里化与反柯里化

<p>    函数柯里化(黑人问号脸)???Currying(黑人问号脸)???妥妥的中式翻译既视感;下面来一起看看究竟什么是函数柯里化:</p>
<p>    维基百科的解释是:把接收多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并返回接受剩余的参数而且返回结果的新函数的技术。其由数学家Haskell Brooks Curry提出,并以curry命名。&nbsp;</p>
<p>    概念往往都是干涩且难懂的,让我们用人话来解释就是:如果我们不确定这个函数有多少个参数,我们可以先给它传入一个参数,然后通过JS闭包(如若不懂JS闭包,请先学习闭包知识点再来学习本篇博文&nbsp;&nbsp;https://www.cnblogs.com/dengyao-blogs/p/11475575.html&nbsp;)来进行返回一个函数,内部函数接收除开第一个参数外的其余参数进行操作并输出,这个就是函数的柯里化;</p>
<p>    举个小例子:</p>
<p>      场景(需求):</p>
<p>      众所周知程序员每天加班的时间还是比较多的,如果我们需要计算一个程序员每天的加班时间,那么我们的第一反应应该是这样;</p>
<p>     </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> overtime=0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> time(x){
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> overtime+=<span style="color: rgba(0, 0, 0, 1)">x;
}

time(</span>1);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1</span>
time(2);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3</span>
time(3);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">6</span></pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>  上面的代码固然没有问题,可是需要每天调用都算加一下当天的时间,很麻烦,并且每调用一次函数都要进行一定的操作,如果数据量巨大,有可能会有影响性能的风险,那么有没有可以偷懒又能解决问题的办法呢?有的!</p>
<p>  </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> time(x){
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(y){
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> x+<span style="color: rgba(0, 0, 0, 1)">y;
    }      
}

</span><span style="color: rgba(0, 0, 255, 1)">var</span> times=time(0<span style="color: rgba(0, 0, 0, 1)">);
times(</span>3);</pre>
</div>
<p>  但是上面代码依然存在问题,在实际开发中很多时候我们的参数是不确定的,上面代码虽然简单的实现了柯里化的基本操作,但是对于参数不确定的情况是处理不了的;所以存在着函数参数的局限性;不过我们从上面的代码中基本可以知道函数柯里化是个啥意思了;就是一个函数调用的时候只允许传入一个参数,然后通过闭包返回内部函数去处理和接收剩余参数,返回的函数通过闭包的方式记住了time的第一个参数;</p>
<p>  我们再来把代码改造一下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">首先定义一个变量接收函数</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> overtime = (<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个数组用来接收参数</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> args =<span style="color: rgba(0, 0, 0, 1)"> [];
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里运用闭包,调用外部函数返回一个内部函数</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">arguments是浏览器内置对象,专门用来接收参数</span>
  <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果参数的长度为0即没有参数的时候</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span>(arguments.length === 0<span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义变量用来累加</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> time = 0<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">循环累加,用i和args的长度进行比较</span>
      <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> i = 0, l = args.length; i &lt; l; i++<span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">进行累加操作   等价于time=time+args</span>
      time +=<span style="color: rgba(0, 0, 0, 1)"> args;
      }
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回累加的结果</span>
      <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> time;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果arguments对象参数长度不为零,即有参数的时候</span>
    }<span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义的空数组添加arguments参数作为数组项,第一个参数古args作为改变this指向,第二个参数arguments把剩余参数作为数组形式添加至空数组中</span>
<span style="color: rgba(0, 0, 0, 1)">      [].push.apply(args, arguments);
    }
}
})();

overtime(</span>3.5);    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第一天</span>
overtime(4.5);    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第二天</span>
overtime(2.1);    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第三天</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">...</span>
<span style="color: rgba(0, 0, 0, 1)">
console.log( overtime() );    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 10.1</span></pre>
</div>
<p>&nbsp;</p>
<p>  代码经过我们的改造已经实现了功能,但是这不是一个函数柯里化的完整实现,那么我们要怎么完整实现呢?下面我们来介绍一种通用的实现方式:</p>
<p>  <strong>通用的实现方式:</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义方法currying,先传入一个参数</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> currying=<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(fn){
  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义空数组装arguments对象的剩余参数</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> args=<span style="color: rgba(0, 0, 0, 1)">[];
  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">利用闭包返回一个函数处理剩余参数</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (){
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果arguments的参数长度为0,即没有剩余参数</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span>(arguments.length===0<span style="color: rgba(0, 0, 0, 1)">){
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">执行上面方法<br>    //这里的this指向下面的s,类似于s(),代表参数长度为0的时候直接调用函数</span>
      <span style="color: rgba(0, 0, 255, 1)">return</span> fn.apply(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">,args)
    }
    console.log(arguments)
  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果arguments的参数长度不为0,即还有剩余参数</span>
  <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在数组的原型对象上添加数组,apply用来更改this的指向为args</span>
  <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将[].slice.call(arguments)的数组添加到原型数组上<br></span>  //这里的[].slice.call(arguments)===Array.prototype.slice.call(arguments)实质上就是将arguments对象转成数组并具有slice功能<br>
<span style="color: rgba(0, 0, 0, 1)">   Array.prototype.push.apply(args,[].slice.call(arguments))
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">args.push([].slice.call(arguments))</span>
<span style="color: rgba(0, 0, 0, 1)">    console.log(args)
  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里返回的arguments.callee是返回的闭包函数,callee是arguments对象里面的一个属性,用于返回正被执行的function对象</span>
    <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> arguments.callee
}
}
  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里调用currying方法并传入add函数,结果会返回闭包内部函数</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> s=<span style="color: rgba(0, 0, 0, 1)">currying(add);
  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用闭包内部函数,当有参数的时候会将参数逐步添加到args数组中,待没有参数传入的时候直接调用</span>
  <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用的时候支持链式操作</span>
s(1)(2)(3<span style="color: rgba(0, 0, 0, 1)">)();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">也可以一次性传入多个参数</span>
   s(1,2,3<span style="color: rgba(0, 0, 0, 1)">);
console.log(s());</span></pre>
</div>
<p>&nbsp;</p>
<p>  </p>
<p>  JS函数柯里化的优点:</p>
<p>    1.可以延迟计算,即如果调用柯里化函数传入参数是不调用的,会将参数添加到数组中存储,等到没有参数传入的时候进行调用;</p>
<p>    2.参数复用,当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么该函数可能是一个很好的柯里化候选。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p> <br>  世间万物相对,有因必有果,当然了,有柯里化必然有反柯里化;</p>
<p>  反柯里化(<strong>uncurrying</strong>),从字面意思上来讲就是跟柯里化的意思相反;其实真正的反柯里化的作用是扩大适用范围,就是说当我们调用某个方法的时候,不需要考虑这个对象自身在设计的过程中有没有这个方法,只要这个方法适用于它,我们就可以使用;(这里引用的是动态语言中的鸭子类型的思想)</p>
<p>&nbsp;</p>
<p>  在学习JS反柯里化之前,我们先学习一下动态语言的鸭子类型思想,以助于我们更好的理解:</p>
<p>  <strong>动态语言鸭子类型思想(维基百科解释):</strong></p>
<p>    在程序设计中,鸭子类型(duck typing)是动态类型的一种风格。</p>
<p>    在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。</p>
<p>    这个概念的名字来源于由 James Whitcomb Riley 提出的鸭子测试,“鸭子测试”可以这样表述:</p>
<p>    &nbsp;&nbsp;当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。</p>
<p>  理论上的解释往往干涩难懂,换成人话来说就是:你是你妈妈的儿子/女儿,不管你是否优秀,是否漂亮,只要你是你妈亲生的,那么你就是你妈的儿子/女儿;换成鸭子类型就是,只要你会呱呱叫,走起来像鸭子,只要你拥有的行为像鸭子,不管你是不是鸭子,那么你就可以被称为鸭子;</p>
<p>    在Javascript中有很多鸭子类型的引用,比如我们在对一个变量进行赋值的时候,显然是不需要考虑变量的类型的,正是因为如此,Javascript才更加的灵活,所以Javascript是一门典型的动态类型语言;</p>
<p>  我们来看一下反柯里化中是怎么引用鸭子类型的:</p>
<p>  </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">函数原型对象上添加uncurring方法</span>
Function.prototype.uncurring = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">改变this的指向    </span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">这里的this指向是Array.prototype.push</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> self = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里的闭包用来返回内部函数的执行</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建一个变量,在数组的原型对象上添加shift上面删除第一个参数</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">改变数组this的指向为arguments</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span> obj =<span style="color: rgba(0, 0, 0, 1)"> Array.prototype.shift.call(arguments);
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">最后返回执行并给方法改变指向为obj也就是arguments</span>
   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 并传入arguments作为参数</span>
    <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> self.apply(obj, arguments);
};
};

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">数组原型对象上添加uncurrying方法</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> push =<span style="color: rgba(0, 0, 0, 1)"> Array.prototype.push.uncurring();

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">测试一下</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">匿名函数自执行</span>
(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里的push就是一个函数方法了</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">相当于传入参数arguments和4两个参数,但是在上面shift方法中删除第一个参数,这里的arguments参数被截取了,所以最后实际上只传入了4</span>
push(arguments, 4<span style="color: rgba(0, 0, 0, 1)">);
console.log(arguments); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"></span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">匿名函数自调用并带入参数1,2,3</span>
})(1, 2, 3)</pre>
</div>
<p> </p>
<p>  到这里大家可以想一想arguments是一个接收参数的对象,里面是没有push方法的,那么arguments为什么能调用push方法呢?</p>
<p>  这是因为代码<strong>var push = Array.prototype.push.uncurring();</strong>在数组的原型对象的push方法上添加了uncurring方法,然后在执行匿名函数的方法<strong>push(arguments, 4);</strong>时候实质上是在调用上面的方法在Function的原型对象上添加uncurring方法并返回一个闭包内部函数执行,在执行的过程中因为Array原型对象上的shift方法会把&nbsp;<strong>push(arguments, 4);</strong>中的arguments截取,所以其实方法的实际调用是push(4),所以最终的结果才是</p>
<p>&nbsp;  </p>
<p>  <span style="font-size: 16px">在《JavaScript设计模式与开发实践》一书中,JS函数的反柯里化的案例是这样写的:</span></p>
<p><span style="font-size: 16px">  </span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个对象</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> obj =<span style="color: rgba(0, 0, 0, 1)"> {
    </span>"length":1<span style="color: rgba(0, 0, 0, 1)">,
    </span>"0":1<span style="color: rgba(0, 0, 0, 1)">
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在Function原型对象定义方法uncurrying</span>
Function.prototype.uncurrying = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">this指向Array.prototype.push</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span> self = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">闭包返回一个内部函数</span>
    <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这里可以拆开理解</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">首先执行apply return </span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Function.prototype.call(Array.prototype.push)</span>
   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">然后Array.prototype.push.call(obj,2)</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">call改变指向obj.push(2)</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">所以最后结果就是{0: 1, 1: 2, length: 2}</span>
      <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Function.prototype.call.apply(self, arguments);
}
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> push =<span style="color: rgba(0, 0, 0, 1)"> Array.prototype.push.uncurrying()

push(obj, </span>2<span style="color: rgba(0, 0, 0, 1)">)
console.log(obj);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">{0: 1, 1: 2, length: 2}</span></pre>
</div>
<p>&nbsp; &nbsp;  <span style="font-size: 16px">上面的方式不好理解?没关系,咱们来个好理解的:</span><span style="font-size: 16px"><br></span></p>
<p><span style="font-size: 16px">  </span></p>
<div class="cnblogs_code">
<pre>Function.prototype.unCurrying = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> self = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {<br>    //[].slice.call(arguments,1)===Array.prototype.push.slice.call(arguments,1)===arguments.slice(1)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> self.apply(arguments, [].slice.call(arguments, 1<span style="color: rgba(0, 0, 0, 1)">));
    };
};



</span><span style="color: rgba(0, 0, 255, 1)">var</span> push =<span style="color: rgba(0, 0, 0, 1)"> Array.prototype.push.uncurrying()
console.log(push);
push(obj,</span>2) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">{0: 1, 1: 2, length: 2}</span>
console.log(obj);</pre>
</div>
<p>  <span style="font-size: 16px">分析一下:</span></p>
<p><span style="font-size: 16px">    1、首先在Function原型对象上添加uncurrying方法,这样所有的Function都可以借用;</span></p>
<p><span style="font-size: 16px">    2、返回一个闭包内部函数</span></p>
<p><span style="font-size: 16px">    3、闭包函数返回的结果中返回的是调用方法,self指向Array.prototype.push,apply方法中第一个参数是更改指向,对应下面push(obj,2)相当于更改指向为obj.push(2)</span></p>
<p><span style="font-size: 16px">    4、apply方法中第二个参数的call方法是更改指向为arguments,并且arguments中能使用slice方法,等于arguments.slice(1)</span></p>

</div>
<div id="MySignature" role="contentinfo">
    <div>作者:有梦想的咸鱼前端</div>
<div>出处:https://www.cnblogs.com/dengyao-blogs/</div>
<div>本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div><br><br>
来源:https://www.cnblogs.com/dengyao-blogs/p/11495861.html
頁: [1]
查看完整版本: 简单粗暴详细讲解javascript实现函数柯里化与反柯里化