목차
什么是柯里化?
为什么要对函数进行柯里化?
如何对函数进行柯里化?
柯里化的一些应用场景
关于柯里化的性能
关于本章一些知识点的解释
一些你可能会喜欢的JS库
웹 프론트엔드 JS 튜토리얼 JavaScript 함수의 커링을 이해하고 마스터할 수 있도록 안내합니다.

JavaScript 함수의 커링을 이해하고 마스터할 수 있도록 안내합니다.

Jun 29, 2020 am 09:57 AM
javascript 카레링

Haskell과 scala는 모두 함수의 커링을 지원합니다. JavaScript 함수의 커링도 JavaScript 함수형 프로그래밍과 밀접한 관련이 있습니다. 관심이 있으시면 이러한 측면을 더 많이 이해하시면 도움이 될 것입니다.

이 글을 읽기 위해 알아야 할 몇 가지 지식 포인트

call/apply/arguments
  • 함수 부분의 마무리 call/apply/arguments
  • 闭包
  • 高阶函数
  • 不完全函数

文章后面有对这些知识的简单解释,大家可以看看.

什么是柯里化?

我们先来看看维基百科中是如何定义的:在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

我们可以举个简单的例子,如下函数add是一般的一个函数,就是将传进来的参数ab相加;函数curryingAdd就是对函数add进行柯里化的函数;
这样一来,原来我们需要直接传进去两个参数来进行运算的函数,现在需要分别传入参数ab,函数如下:

function add(a, b) {
    return a + b;
}

function curryingAdd(a) {
    return function(b) {
        return a + b;
    }
}

add(1, 2); // 3
curryingAdd(1)(2); // 3
로그인 후 복사

看到这里你可能会想,这样做有什么用?为什么要这样做?这样做能够给我们的应用带来什么样的好处?先别着急,我们接着往下看.

为什么要对函数进行柯里化?

  • 可以使用一些小技巧(见下文)
  • 提前绑定好函数里面的某些参数,达到参数复用的效果,提高了适用性.
  • 固定易变因素
  • 延迟计算

总之,函数的柯里化能够让你重新组合你的应用,把你的复杂功能拆分成一个一个的小部分,每一个小的部分都是简单的,便于理解的,而且是容易测试的;

如何对函数进行柯里化?

在这一部分里,我们由浅入深的一步步来告诉大家如何对一个多参数的函数进行柯里化.其中用到的知识有闭包,高阶函数,不完全函数等等.

  • I 开胃菜

    假如我们要实现一个功能,就是输出语句name喜欢song,其中namesong都是可变参数;那么一般情况下我们会这样写:

    function printInfo(name, song) {
        console.log(name + '喜欢的歌曲是: ' + song);
    }
    printInfo('Tom', '七里香');
    printInfo('Jerry', '雅俗共赏');
    로그인 후 복사

    对上面的函数进行柯里化之后,我们可以这样写:

    function curryingPrintInfo(name) {
        return function(song) {
            console.log(name + '喜欢的歌曲是: ' + song);
        }
    }
    var tomLike = curryingPrintInfo('Tom');
    tomLike('七里香');
    var jerryLike = curryingPrintInfo('Jerry');
    jerryLike('雅俗共赏');
    로그인 후 복사
  • II 小鸡炖蘑菇

    上面我们虽然对对函数printInfo进行了柯里化,但是我们可不想在需要柯里化的时候,都像上面那样不断地进行函数的嵌套,那简直是噩梦;
    所以我们要创造一些帮助其它函数进行柯里化的函数,我们暂且叫它为curryingHelper吧,一个简单的curryingHelper函数如下所示:

    function curryingHelper(fn) {
        var _args = Array.prototype.slice.call(arguments, 1);
        return function() {
            var _newArgs = Array.prototype.slice.call(arguments);
            var _totalArgs = _args.concat(_newArgs);
            return fn.apply(this, _totalArgs);
        }
    }
    로그인 후 복사

    这里解释一点东西,首先函数的arguments表示的是传递到函数中的参数对象,它不是一个数组,它是一个类数组对象;
    所以我们可以使用函数的Array.prototype.slice方法,然后使用.call方法来获取arguments里面的内容.
    我们使用fn.apply(this, _totalArgs)来给函数fn传递正确的参数.

    接下来我们来写一个简单的函数验证上面的辅助柯里化函数的正确性, 代码部分如下:

    function showMsg(name, age, fruit) {
        console.log('My name is ' + name + ', I\'m ' + age + ' years old, ' + ' and I like eat ' + fruit);
    }
    
    var curryingShowMsg1 = curryingHelper(showMsg, 'dreamapple');
    curryingShowMsg1(22, 'apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple
    
    var curryingShowMsg2 = curryingHelper(showMsg, 'dreamapple', 20);
    curryingShowMsg2('watermelon'); // My name is dreamapple, I'm 20 years old,  and I like eat watermelon
    로그인 후 복사

    上面的结果表示,我们的这个柯里化的函数是正确的.上面的curryingHelper就是一个高阶函数,关于高阶函数的解释可以参照下文.

  • III 牛肉火锅

    上面的柯里化帮助函数确实已经能够达到我们的一般性需求了,但是它还不够好,我们希望那些经过柯里化后的函数可以每次只传递进去一个参数,
    然后可以进行多次参数的传递,那么应该怎么办呢?我们可以再花费一些脑筋,写出一个betterCurryingHelper函数,实现我们上面说的那些
    功能.代码如下:

    function betterCurryingHelper(fn, len) {
        var length = len || fn.length;
        return function () {
            var allArgsFulfilled = (arguments.length >= length);
    
            // 如果参数全部满足,就可以终止递归调用
            if (allArgsFulfilled) {
                return fn.apply(this, arguments);
            }
            else {
                var argsNeedFulfilled = [fn].concat(Array.prototype.slice.call(arguments));
                return betterCurryingHelper(curryingHelper.apply(this, argsNeedFulfilled), length - arguments.length);
            }
        };
    }
    로그인 후 복사

    其中curryingHelper就是上面II 小鸡炖蘑菇中提及的那个函数.需要注意的是fn.length表示的是这个函数的参数长度.
    接下来我们来检验一下这个函数的正确性:

    var betterShowMsg = betterCurryingHelper(showMsg);
    betterShowMsg('dreamapple', 22, 'apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple
    betterShowMsg('dreamapple', 22)('apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple
    betterShowMsg('dreamapple')(22, 'apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple
    betterShowMsg('dreamapple')(22)('apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple
    로그인 후 복사

    其中showMsg就是II 小鸡炖蘑菇部分提及的那个函数.
    我们可以看出来,这个betterCurryingHelper

    고차함수
  • 불완전함수

이 지식에 대한 간단한 설명이 글 마지막에 있으니 한번 보시면 됩니다.커링이 뭐죠?🎜🎜먼저 위키피디아부터 살펴볼까요? 정의 방법:🎜컴퓨터 과학에서 Currying(영어: Currying)은 Currying 또는 Garrying으로도 번역되며, 여러 매개변수를 허용하는 함수를 단일 매개변수(원래 함수의 첫 번째 매개변수)를 허용하는 함수로 변환하는 것입니다. ) 나머지 매개변수를 받아들이고 결과를 반환하는 새 함수를 반환합니다. 🎜🎜🎜다음 함수 addab 매개변수를 전달하는 일반 함수입니다. ; curryingAdd 함수는 add 함수를 커리하는 함수입니다.
이런 방식으로 우리는 다음을 수행하기 위해 두 개의 매개변수를 직접 전달해야 한다는 것이 밝혀졌습니다. 이제 매개변수 ab를 각각 전달해야 합니다. 함수는 다음과 같습니다. 🎜
var _ = {};
function crazyCurryingHelper(fn, length, args, holes) {
    length = length || fn.length;
    args   = args   || [];
    holes  = holes  || [];

    return function() {
        var _args       = args.slice(),
            _holes      = holes.slice();

        // 存储接收到的args和holes的长度
        var argLength   = _args.length,
            holeLength  = _holes.length;

        var allArgumentsSpecified = false;

        // 循环
        var arg     = null,
            i       = 0,
            aLength = arguments.length;

        for(; i < aLength; i++) {
            arg = arguments[i];

            if(arg === _ && holeLength) {
                // 循环holes的位置
                holeLength--;
                _holes.push(_holes.shift());
            } else if (arg === _) {
                // 存储hole就是_的位置
                _holes.push(argLength + i);
            } else if (holeLength) {
                // 是否还有没有填补的hole
                // 在参数列表指定hole的地方插入当前参数
                holeLength--;
                _args.splice(_holes.shift(), 0, arg);
            } else {
                // 不需要填补hole,直接添加到参数列表里面
                _args.push(arg);
            }
        }

        // 判断是否所有的参数都已满足
        allArgumentsSpecified = (_args.length >= length);
        if(allArgumentsSpecified) {
            return fn.apply(this, _args);
        }

        // 递归的进行柯里化
        return crazyCurryingHelper.call(this, fn, length, _args, _holes);
    };
}
로그인 후 복사
로그인 후 복사
🎜이것을 보면 무엇인지 생각할 수 있습니다. 이것을 사용하는 이유는 무엇입니까? 이것이 우리 애플리케이션에 어떤 이점을 가져올 수 있습니까? 걱정하지 마세요. 🎜🎜카레 기능이 필요한 이유는 무엇입니까? (아래 참조)🎜 🎜미리 함수에 특정 매개변수를 바인딩하면 매개변수 재사용 효과를 얻고 적용성이 향상됩니다. 🎜🎜가변 요소 고정 🎜🎜지연 계산🎜🎜간단히 말하면, 함수 커링을 사용하면 재구성이 가능합니다. 복잡한 기능을 작은 부분으로 나누세요. 각각의 작은 부분은 간단하고 이해하기 쉽고 테스트하기 쉽습니다. 🎜🎜함수를 카레하는 방법 🎜🎜이 부분에서는 카레를 만드는 방법을 단계별로 알려드리겠습니다. 다중 매개변수 함수에는 클로저, 고차 함수, 불완전 함수 등이 포함됩니다. 🎜🎜🎜🎜🎜I appetizer🎜 🎜🎜함수를 구현하려면 song과 같이 name 문을 출력하면 됩니다. 여기서 namesong code>는 둘 다 변수 매개변수입니다. 그러면 일반적으로 다음과 같이 작성합니다. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">var crazyShowMsg = crazyCurryingHelper(showMsg); crazyShowMsg(_, 22)('dreamapple')('apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple crazyShowMsg( _, 22, 'apple')('dreamapple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple crazyShowMsg( _, 22, _)('dreamapple', _, 'apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple crazyShowMsg( 'dreamapple', _, _)(22)('apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple crazyShowMsg('dreamapple')(22)('apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple</pre><div class="contentsignin">로그인 후 복사</div></div><div class="contentsignin">로그인 후 복사</div></div>🎜위 함수를 구성합니다. 카레를 사용한 후 다음과 같이 작성할 수 있습니다. 위의 <code>printInfo에서는 카레가 필요할 때 카레를 사용하고 싶지 않습니다. 경화할 때 위와 같은 함수를 계속 중첩하는 것은 악몽입니다.
그래서 몇 가지를 만들어야 합니다. 다른 함수의 카레를 돕는 함수를 curryingHelper라고 부르자. 간단한 curryingHelper 함수는 다음과 같다. 🎜
function hello(name) {
    console.log('Hello, ' + name);
}
setTimeout(hello('dreamapple'), 3600); //立即执行,不会在3.6s后执行
setTimeout(function() {
    hello('dreamapple');
}, 3600); // 3.6s 后执行
로그인 후 복사
로그인 후 복사
🎜 먼저 에 대해 설명해보자. 함수의 >인수는 함수에 전달된 매개변수를 나타냅니다. 객체는 배열이 아니며 배열과 유사한 객체입니다.
따라서 함수의 Array.prototype을 사용할 수 있습니다. 슬라이스 메소드를 사용하고 .call 메소드를 사용하여 인수의 내용을 가져옵니다.
우리는 fn.apply(this, _totalArgs)를 사용하여 올바른 매개변수를 fn 함수에 전달합니다.🎜🎜다음으로 위의 보조 커링 함수의 정확성을 확인하는 간단한 함수를 작성해 보겠습니다. 코드 부분은 다음과 같습니다. 🎜
setTimeout(hello.bind(this, 'dreamapple'), 3600); // 3.6s 之后执行函数
로그인 후 복사
로그인 후 복사
🎜위 결과는 위의 curryingHelper가 🎜고차 함수🎜임을 나타냅니다. 고차 함수에 대한 설명은 다음을 참조하세요.🎜🎜 🎜🎜🎜III Beef Hot Pot🎜🎜🎜위의 카레 도우미 함수는 실제로 우리의 목표를 달성할 수 있습니다. 일반적인 요구 사항이지만 충분하지 않습니다. br/>그러면 여러 매개변수를 전달할 수 있습니다. 그러면 우리는 무엇을 해야 할까요? 위에서 언급한
함수를 구현하려면 좀 더 두뇌를 사용하여 betterCurryingHelper 함수를 작성하면 됩니다. 🎜
setTimeout(curryingHelper(hello, 'dreamapple'), 3600); // 其中curryingHelper是上面已经提及过的
로그인 후 복사
로그인 후 복사
🎜여기서 curryingHelper는 위 🎜II 버섯을 곁들인 닭고기 조림🎜에서 언급한 함수입니다. fn.length는 의 매개변수 길이를 나타냅니다. 이 함수입니다.
다음으로 이 함수가 올바른지 확인해 보겠습니다. 🎜
function multiply(x) {
    var y = function(x) {
        return multiply(x * y);
    };
    y.toString = y.valueOf = function() {
        return x;
    };
    return y;
}

console.log(multiply(1)(2)(3) == 6); // true
console.log(multiply(1)(2)(3)(4)(5) == 120); // true
로그인 후 복사
로그인 후 복사
🎜여기서 showMsg는 🎜II 버섯 닭고기 조림🎜 부분에서 언급한 함수입니다.
betterCurryingHelper가 실제로 우리가 원하는 기능을 구현한 것을 볼 수 있습니다. 그리고 원래 기능처럼 카레 기능을 사용할 수도 있습니다.🎜🎜🎜🎜🎜IV 절인 고추 닭발🎜🎜

我们已经能够写出很好的柯里化辅助函数了,但是这还不算是最刺激的,如果我们在传递参数的时候可以不按照顺来那一定很酷;当然我们也可以写出这样的函数来,
这个crazyCurryingHelper函数如下所示:

var _ = {};
function crazyCurryingHelper(fn, length, args, holes) {
    length = length || fn.length;
    args   = args   || [];
    holes  = holes  || [];

    return function() {
        var _args       = args.slice(),
            _holes      = holes.slice();

        // 存储接收到的args和holes的长度
        var argLength   = _args.length,
            holeLength  = _holes.length;

        var allArgumentsSpecified = false;

        // 循环
        var arg     = null,
            i       = 0,
            aLength = arguments.length;

        for(; i < aLength; i++) {
            arg = arguments[i];

            if(arg === _ && holeLength) {
                // 循环holes的位置
                holeLength--;
                _holes.push(_holes.shift());
            } else if (arg === _) {
                // 存储hole就是_的位置
                _holes.push(argLength + i);
            } else if (holeLength) {
                // 是否还有没有填补的hole
                // 在参数列表指定hole的地方插入当前参数
                holeLength--;
                _args.splice(_holes.shift(), 0, arg);
            } else {
                // 不需要填补hole,直接添加到参数列表里面
                _args.push(arg);
            }
        }

        // 判断是否所有的参数都已满足
        allArgumentsSpecified = (_args.length >= length);
        if(allArgumentsSpecified) {
            return fn.apply(this, _args);
        }

        // 递归的进行柯里化
        return crazyCurryingHelper.call(this, fn, length, _args, _holes);
    };
}
로그인 후 복사
로그인 후 복사

一些解释,我们使用_来表示参数中的那些缺失的参数,如果你使用了lodash的话,会有冲突的;那么你可以使用别的符号替代.
按照一贯的尿性,我们还是要验证一下这个crazyCurryingHelper是不是实现了我们所说的哪些功能,代码如下:

var crazyShowMsg = crazyCurryingHelper(showMsg);
crazyShowMsg(_, 22)('dreamapple')('apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple
crazyShowMsg( _, 22, 'apple')('dreamapple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple
crazyShowMsg( _, 22, _)('dreamapple', _, 'apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple
crazyShowMsg( 'dreamapple', _, _)(22)('apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple
crazyShowMsg('dreamapple')(22)('apple'); // My name is dreamapple, I'm 22 years old,  and I like eat apple
로그인 후 복사
로그인 후 복사

结果显示,我们这个函数也实现了我们所说的那些功能.

柯里化的一些应用场景

说了那么多,其实这部分才是最重要的部分;学习某个知识要一定可以用得到,不然学习它干嘛.

  • 关于函数柯里化的一些小技巧

    • setTimeout传递地进来的函数添加参数

      一般情况下,我们如果想给一个setTimeout传递进来的函数添加参数的话,一般会使用之种方法:

      function hello(name) {
          console.log('Hello, ' + name);
      }
      setTimeout(hello('dreamapple'), 3600); //立即执行,不会在3.6s后执行
      setTimeout(function() {
          hello('dreamapple');
      }, 3600); // 3.6s 后执行
      로그인 후 복사
      로그인 후 복사

      我们使用了一个新的匿名函数包裹我们要执行的函数,然后在函数体里面给那个函数传递参数值.

      当然,在ES5里面,我们也可以使用函数的bind方法,如下所示:

      setTimeout(hello.bind(this, 'dreamapple'), 3600); // 3.6s 之后执行函数
      로그인 후 복사
      로그인 후 복사

      这样也是非常的方便快捷,并且可以绑定函数执行的上下文.

      我们本篇文章是讨论函数的柯里化,当然我们这里也可以使用函数的柯里化来达到这个效果:

      setTimeout(curryingHelper(hello, 'dreamapple'), 3600); // 其中curryingHelper是上面已经提及过的
      로그인 후 복사
      로그인 후 복사

      这样也是可以的,是不是很酷.其实函数的bind方法也是使用函数的柯里化来完成的,详情可以看这里Function.prototype.bind().

    • 写出这样一个函数multiply(1)(2)(3) == 6结果为true,multiply(1)(2)(3)(...)(n) == (1)*(2)*(3)*(...)*(n)结果为true

      这个题目不知道大家碰到过没有,不过通过函数的柯里化,也是有办法解决的,看下面的代码:

      function multiply(x) {
          var y = function(x) {
              return multiply(x * y);
          };
          y.toString = y.valueOf = function() {
              return x;
          };
          return y;
      }
      
      console.log(multiply(1)(2)(3) == 6); // true
      console.log(multiply(1)(2)(3)(4)(5) == 120); // true
      로그인 후 복사
      로그인 후 복사

      因为multiply(1)(2)(3)的直接结果并不是6,而是一个函数对象{ [Number: 6] valueOf: [Function], toString: [Function] },我们
      之后使用了==会将左边这个函数对象转换成为一个数字,所以就达到了我们想要的结果.还有关于为什么使用toStringvalueOf方法
      可以看看这里的解释Number.prototype.valueOf(),Function.prototype.toString().

    • 上面的那个函数不够纯粹,我们也可以实现一个更纯粹的函数,但是可以会不太符合题目的要求.
      我们可以这样做,先把函数的参数存储,然后再对这些参数做处理,一旦有了这个思路,我们就不难写出些面的代码:

      function add() {
          var args = Array.prototype.slice.call(arguments);
          var _that = this;
          return function() {
              var newArgs = Array.prototype.slice.call(arguments);
              var total = args.concat(newArgs);
              if(!arguments.length) {
                  var result = 1;
                  for(var i = 0; i < total.length; i++) {
                      result *= total[i];
                  }
                  return result;
              }
              else {
                  return add.apply(_that, total);
              }
          }
      }
      add(1)(2)(3)(); // 6
      add(1, 2, 3)(); // 6
      로그인 후 복사
    • 当我们的需要兼容IE9之前版本的IE浏览器的话,我们可能需要写出一些兼容的方案 ,比如事件监听;一般情况下我们应该会这样写:

      var addEvent = function (el, type, fn, capture) {
              if (window.addEventListener) {
                  el.addEventListener(type, fn, capture);
              }
              else {
                  el.attachEvent('on' + type, fn);
              }
          };
      로그인 후 복사

      这也写也是可以的,但是性能上会差一点,因为如果是在低版本的IE浏览器上每一次都会运行if()语句,产生了不必要的性能开销.
      我们也可以这样写:

      var addEvent = (function () {
              if (window.addEventListener) {
                  return function (el, type, fn, capture) {
                      el.addEventListener(type, fn, capture);
                  }
              }
              else {
                  return function (el, type, fn) {
                      var IEtype = 'on' + type;
                      el.attachEvent(IEtype, fn);
                  }
              }
          })();
      로그인 후 복사

      这样就减少了不必要的开支,整个函数运行一次就可以了.

  • 延迟计算

    上面的那两个函数multiply()add()实际上就是延迟计算的例子.

  • 提前绑定好函数里面的某些参数,达到参数复用的效果,提高了适用性.

    我们的I 开胃菜部分的tomLikejerryLike其实就是属于这种的,绑定好函数里面的第一个参数,然后后面根据情况分别使用不同的函数.

  • 固定易变因素

    我们经常使用的函数的bind方法就是一个固定易变因素的很好的例子.

关于柯里化的性能

当然,使用柯里化意味着有一些额外的开销;这些开销一般涉及到这些方面,首先是关于函数参数的调用,操作arguments对象通常会比操作命名的参数要慢一点;
还有,在一些老的版本的浏览器中arguments.length的实现是很慢的;直接调用函数fn要比使用fn.apply()或者fn.call()要快一点;产生大量的嵌套
作用域还有闭包会带来一些性能还有速度的降低.但是,大多数的web应用的性能瓶颈时发生在操作DOM上的,所以上面的那些开销比起DOM操作的开销还是比较小的.

关于本章一些知识点的解释

  • 琐碎的知识点

    fn.length: 表示的是这个函数中参数的个数.

    arguments.callee: 指向的是当前运行的函数.calleearguments对象的属性。
    在该函数的函数体内,它可以指向当前正在执行的函数.当函数是匿名函数时,这是很有用的,比如没有名字的函数表达式(也被叫做"匿名函数").
    详细解释可以看这里arguments.callee.我们可以看一下下面的例子:

    function hello() {
        return function() {
            console.log('hello');
            if(!arguments.length) {
                console.log('from a anonymous function.');
                return arguments.callee;
            }
        }
    }
    
    hello()(1); // hello
    
    /*
     * hello
     * from a anonymous function.
     * hello
     * from a anonymous function.
     */
    hello()()();
    로그인 후 복사

    fn.caller: 返回调用指定函数的函数.详细的解释可以看这里Function.caller,下面是示例代码:

    function hello() {
        console.log('hello');
        console.log(hello.caller);
    }
    
    function callHello(fn) {
        return fn();
    }
    
    callHello(hello); // hello [Function: callHello]
    로그인 후 복사
  • 高阶函数(high-order function)

    高阶函数就是操作函数的函数,它接受一个或多个函数作为参数,并返回一个新的函数.
    我们来看一个例子,来帮助我们理解这个概念.就举一个我们高中经常遇到的场景,如下:

    f1(x, y) = x + y;
    f2(x) = x * x;
    f3 = f2(f3(x, y));
    로그인 후 복사

    我们来实现f3函数,看看应该如何实现,具体的代码如下所示:

    function f1(x, y) {
        return x + y;
    }
    
    function f2(x) {
        return x * x;
    }
    
    function func3(func1, func2) {
        return function() {
            return func2.call(this, func1.apply(this, arguments));
        }
    }
    
    var f3 = func3(f1, f2);
    console.log(f3(2, 3)); // 25
    로그인 후 복사

    我们通过函数func3将函数f1,f2结合到了一起,然后返回了一个新的函数f3;这个函数就是我们期望的那个函数.

  • 不完全函数(partial function)

    什么是不完全函数呢?所谓的不完全函数和我们上面所说的柯里化基本差不多;所谓的不完全函数,就是给你想要运行的那个函数绑定一个固定的参数值;
    然后后面的运行或者说传递参数都是在前面的基础上进行运行的.看下面的例子:

    // 一个将函数的arguments对象变成一个数组的方法
    function array(a, n) {
        return Array.prototype.slice.call(a, n || 0);
    }
    // 我们要运行的函数
    function showMsg(a, b, c){
        return a * (b - c);
    }
    function partialLeft(f) {
        var args = arguments;
        return function() {
            var a = array(args, 1);
            a = a.concat(array(arguments));
            console.log(a); // 打印实际传递到函数中的参数列表
            return f.apply(this, a);
        }
    }
    function partialRight(f) {
        var args = arguments;
        return function() {
            var a = array(arguments);
            a = a.concat(array(args, 1));
            console.log(a); // 打印实际传递到函数中的参数列表
            return f.apply(this, a);
        }
    }
    function partial(f) {
        var args = arguments;
        return function() {
            var a = array(args, 1);
            var i = 0; j = 0;
            for(; i < a.length; i++) {
                if(a[i] === undefined) {
                    a[i] = arguments[j++];
                }
            }
            a = a.concat(array(arguments, j));
            console.log(a); // 打印实际传递到函数中的参数列表
            return f.apply(this, a);
        }
    }
    partialLeft(showMsg, 1)(2, 3); // 实际参数列表: [1, 2, 3] 所以结果是 1 * (2 - 3) = -1
    partialRight(showMsg, 1)(2, 3); // 实际参数列表: [2, 3, 1] 所以结果是 2 * (3 - 1) = 4
    partial(showMsg, undefined, 1)(2, 3); // 实际参数列表: [2, 1, 3] 所以结果是 2 * (1 - 3) = -4
    로그인 후 복사

一些你可能会喜欢的JS库

JavaScript的柯里化与JavaScript的函数式编程密不可分,下面列举了一些关于JavaScript函数式编程的库,大家可以看一下:

  • underscore
  • lodash
  • ramda
  • bacon.js
  • fn.js
  • functional-js

推荐教程:《JS教程

위 내용은 JavaScript 함수의 커링을 이해하고 마스터할 수 있도록 안내합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 Dec 17, 2023 pm 02:54 PM

WebSocket 및 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 소개: 지속적인 기술 개발로 음성 인식 기술은 인공 지능 분야의 중요한 부분이 되었습니다. WebSocket과 JavaScript를 기반으로 한 온라인 음성 인식 시스템은 낮은 대기 시간, 실시간, 크로스 플랫폼이라는 특징을 갖고 있으며 널리 사용되는 솔루션이 되었습니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법을 소개합니다.

WebSocket 및 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 WebSocket 및 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 Dec 17, 2023 pm 05:30 PM

WebSocket과 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 서론: 인터넷 기술의 급속한 발전과 함께 실시간 모니터링 시스템이 다양한 분야에서 널리 활용되고 있다. 실시간 모니터링을 구현하는 핵심 기술 중 하나는 WebSocket과 JavaScript의 조합입니다. 이 기사에서는 실시간 모니터링 시스템에서 WebSocket 및 JavaScript의 적용을 소개하고 코드 예제를 제공하며 구현 원칙을 자세히 설명합니다. 1. 웹소켓 기술

JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 Dec 17, 2023 pm 12:09 PM

JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 소개: 인터넷의 대중화와 기술의 발전으로 점점 더 많은 레스토랑에서 온라인 주문 서비스를 제공하기 시작했습니다. 실시간 온라인 주문 시스템을 구현하기 위해 JavaScript 및 WebSocket 기술을 사용할 수 있습니다. WebSocket은 TCP 프로토콜을 기반으로 하는 전이중 통신 프로토콜로 클라이언트와 서버 간의 실시간 양방향 통신을 실현할 수 있습니다. 실시간 온라인 주문 시스템에서는 사용자가 요리를 선택하고 주문을 하면

WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 Dec 17, 2023 am 09:39 AM

WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 오늘날의 디지털 시대에는 점점 더 많은 기업과 서비스에서 온라인 예약 기능을 제공해야 합니다. 효율적인 실시간 온라인 예약 시스템을 구현하는 것이 중요합니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. WebSocket이란 무엇입니까? WebSocket은 단일 TCP 연결의 전이중 방식입니다.

JavaScript와 WebSocket: 효율적인 실시간 일기예보 시스템 구축 JavaScript와 WebSocket: 효율적인 실시간 일기예보 시스템 구축 Dec 17, 2023 pm 05:13 PM

JavaScript 및 WebSocket: 효율적인 실시간 일기 예보 시스템 구축 소개: 오늘날 일기 예보의 정확성은 일상 생활과 의사 결정에 매우 중요합니다. 기술이 발전함에 따라 우리는 날씨 데이터를 실시간으로 획득함으로써 보다 정확하고 신뢰할 수 있는 일기예보를 제공할 수 있습니다. 이 기사에서는 JavaScript 및 WebSocket 기술을 사용하여 효율적인 실시간 일기 예보 시스템을 구축하는 방법을 알아봅니다. 이 문서에서는 특정 코드 예제를 통해 구현 프로세스를 보여줍니다. 우리

간단한 JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법 간단한 JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법 Jan 05, 2024 pm 06:08 PM

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

자바스크립트에서 insertBefore를 사용하는 방법 자바스크립트에서 insertBefore를 사용하는 방법 Nov 24, 2023 am 11:56 AM

사용법: JavaScript에서 insertBefore() 메서드는 DOM 트리에 새 노드를 삽입하는 데 사용됩니다. 이 방법에는 삽입할 새 노드와 참조 노드(즉, 새 노드가 삽입될 노드)라는 두 가지 매개 변수가 필요합니다.

JavaScript 및 WebSocket: 효율적인 실시간 이미지 처리 시스템 구축 JavaScript 및 WebSocket: 효율적인 실시간 이미지 처리 시스템 구축 Dec 17, 2023 am 08:41 AM

JavaScript는 웹 개발에 널리 사용되는 프로그래밍 언어인 반면 WebSocket은 실시간 통신에 사용되는 네트워크 프로토콜입니다. 두 가지의 강력한 기능을 결합하면 효율적인 실시간 영상 처리 시스템을 만들 수 있습니다. 이 기사에서는 JavaScript와 WebSocket을 사용하여 이 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 첫째, 실시간 영상처리 시스템의 요구사항과 목표를 명확히 할 필요가 있다. 실시간 이미지 데이터를 수집할 수 있는 카메라 장치가 있다고 가정해 보겠습니다.

See all articles