検索
ホームページウェブフロントエンドjsチュートリアルJavaScript 関数のカリー化を理解し、習得できるようにします。

Haskell と scala はどちらも関数のカリー化をサポートしています。JavaScript 関数のカリー化は JavaScript 関数プログラミングとも密接に関連しています。興味がある場合は、これらの側面をさらに理解して取り組むことができます。多くのことを得ることができると思います.

この記事を読むために知っておく必要があるいくつかの知識ポイント

  • 関数部分 #/ の call/apply##引数
  • クロージャ
  • 高階関数
  • 不完全な関数
これについては後ほど詳しく説明します。記事 簡単な説明をご覧ください。

カリー化とは何ですか?

まず、Wikipedia でどのように定義されているかを見てみましょう:

コンピュータ サイエンスでは、カリー化 カリー化 (英語:Currying)は、カリー化またはカリー化とも訳され、複数のパラメータを受け入れる関数を単一のパラメータ(元の関数の最初のパラメータ)を受け入れる関数に変換し、残りのパラメータを受け入れる関数を返すことです。結果を返す新しい関数の場合。

簡単な例を挙げると、次の関数

add はパラメータ ab## を渡す一般的な関数です。 #Add; functioncurryingAdd は function add;をカリー化する関数です。このようにして、操作を実行するには 2 つのパラメーターを直接渡す必要があることがわかります。ここで、パラメータ a
b をそれぞれ渡す必要があります。関数は次のとおりです: <pre class="brush:php;toolbar:false">function add(a, b) {     return a + b; } function curryingAdd(a) {     return function(b) {         return a + b;     } } add(1, 2); // 3 curryingAdd(1)(2); // 3</pre> これを見た人は、何の役に立つのかと考えているかもしれません。これを行う理由は何ですか?これはアプリケーションにどのような利点をもたらしますか?心配しないで、読み進めましょう。

なぜ関数をカリー化する必要があるのですか?

いくつかのヒント (以下を参照) を使用できます。
  • パラメータの再利用の効果を実現し、適用性を向上させるために、事前に関数内の特定のパラメータをバインドします。
  • 固定変数係数
  • 遅延計算
  • つまり、関数のカリー化により、アプリケーションを再編成し、複雑な関数を小さな部分に分割することができます。それぞれの小さな部分はシンプルで、理解しやすく、テストも簡単です。

関数をカリー化する方法?

このパートでは、関数をカリー化する方法を段階的に説明します。複数パラメータ関数はカリー化されます。使用される知識には、

クロージャ

が含まれます。 、高階関数不完全関数など

  • I前菜

    If関数を実装したいのですが、ステートメント

    name

    Likesong を出力することです。このうち、namesong は可変パラメータです。 ; その後、一般的に次のように書きます: <pre class="brush:php;toolbar:false">function printInfo(name, song) {     console.log(name + '喜欢的歌曲是: ' + song); } printInfo('Tom', '七里香'); printInfo('Jerry', '雅俗共赏');</pre>上記の関数をカリー化した後、次のように書くことができます:

    function curryingPrintInfo(name) {
        return function(song) {
            console.log(name + '喜欢的歌曲是: ' + song);
        }
    }
    var tomLike = curryingPrintInfo('Tom');
    tomLike('七里香');
    var jerryLike = curryingPrintInfo('Jerry');
    jerryLike('雅俗共赏');

  • II チキンのキノコ煮込み

    カリー化で関数

    printInfo

    をカリー化しましたが、カリー化が必要なときに上記のような関数をネストし続けることは望ましくありません。これは単なる悪夢です。したがって、他の関数のカリー化を支援する関数をいくつか作成する必要があります。当面は curryingHelper
    と呼びましょう。単純な curryingHelper 関数は次のとおりです: <pre class="brush:php;toolbar:false">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);     } }</pre> ここで少し説明します、まず、関数の

    arguments

    関数に渡されるパラメータ オブジェクトを表します。これは配列ではなく、配列のようなオブジェクトです。So関数の 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 牛肉鍋
  • 上記のカレーヘルパーこの関数は確かに私たちの一般的なニーズを満たすことができますが、それだけでは十分ではありません。カリー化した後でこれらの関数が機能することを願っています。一度に 1 つのパラメーターのみを渡すことができます。 後はパラメーターを複数回渡すことができます。では、どうすればよいでしょうか。もう少し頭を使って、

    betterCurryingHelper

    関数を作成して、上記の
    関数で述べたことを実現できます。コードは次のとおりです: <pre class="brush:php;toolbar:false">function betterCurryingHelper(fn, len) {     var length = len || fn.length;     return function () {         var allArgsFulfilled = (arguments.length &gt;= 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);         }     }; }</pre> そのうちの
    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 は、私たちが望む関数を実現します。そして、元の関数も使用できます。その関数は、カレー関数も使用します。

    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 = 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 
    • 当我们的需要兼容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 

一些你可能会喜欢的JS库

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

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

推荐教程:《JS教程

以上がJavaScript 関数のカリー化を理解し、習得できるようにします。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事は简书で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。
Python vs. JavaScript:学習曲線と使いやすさPython vs. JavaScript:学習曲線と使いやすさApr 16, 2025 am 12:12 AM

Pythonは、スムーズな学習曲線と簡潔な構文を備えた初心者により適しています。 JavaScriptは、急な学習曲線と柔軟な構文を備えたフロントエンド開発に適しています。 1。Python構文は直感的で、データサイエンスやバックエンド開発に適しています。 2。JavaScriptは柔軟で、フロントエンドおよびサーバー側のプログラミングで広く使用されています。

Python vs. JavaScript:コミュニティ、ライブラリ、リソースPython vs. JavaScript:コミュニティ、ライブラリ、リソースApr 15, 2025 am 12:16 AM

PythonとJavaScriptには、コミュニティ、ライブラリ、リソースの観点から、独自の利点と短所があります。 1)Pythonコミュニティはフレンドリーで初心者に適していますが、フロントエンドの開発リソースはJavaScriptほど豊富ではありません。 2)Pythonはデータサイエンスおよび機械学習ライブラリで強力ですが、JavaScriptはフロントエンド開発ライブラリとフレームワークで優れています。 3)どちらも豊富な学習リソースを持っていますが、Pythonは公式文書から始めるのに適していますが、JavaScriptはMDNWebDocsにより優れています。選択は、プロジェクトのニーズと個人的な関心に基づいている必要があります。

C/CからJavaScriptへ:すべてがどのように機能するかC/CからJavaScriptへ:すべてがどのように機能するかApr 14, 2025 am 12:05 AM

C/CからJavaScriptへのシフトには、動的なタイピング、ゴミ収集、非同期プログラミングへの適応が必要です。 1)C/Cは、手動メモリ管理を必要とする静的に型付けられた言語であり、JavaScriptは動的に型付けされ、ごみ収集が自動的に処理されます。 2)C/Cはマシンコードにコンパイルする必要がありますが、JavaScriptは解釈言語です。 3)JavaScriptは、閉鎖、プロトタイプチェーン、約束などの概念を導入します。これにより、柔軟性と非同期プログラミング機能が向上します。

JavaScriptエンジン:実装の比較JavaScriptエンジン:実装の比較Apr 13, 2025 am 12:05 AM

さまざまなJavaScriptエンジンは、各エンジンの実装原則と最適化戦略が異なるため、JavaScriptコードを解析および実行するときに異なる効果をもたらします。 1。語彙分析:ソースコードを語彙ユニットに変換します。 2。文法分析:抽象的な構文ツリーを生成します。 3。最適化とコンパイル:JITコンパイラを介してマシンコードを生成します。 4。実行:マシンコードを実行します。 V8エンジンはインスタントコンピレーションと非表示クラスを通じて最適化され、Spidermonkeyはタイプ推論システムを使用して、同じコードで異なるパフォーマンスパフォーマンスをもたらします。

ブラウザを超えて:現実世界のJavaScriptブラウザを超えて:現実世界のJavaScriptApr 12, 2025 am 12:06 AM

現実世界におけるJavaScriptのアプリケーションには、サーバー側のプログラミング、モバイルアプリケーション開発、モノのインターネット制御が含まれます。 2。モバイルアプリケーションの開発は、ReactNativeを通じて実行され、クロスプラットフォームの展開をサポートします。 3.ハードウェアの相互作用に適したJohnny-Fiveライブラリを介したIoTデバイス制御に使用されます。

next.jsを使用してマルチテナントSaaSアプリケーションを構築する(バックエンド統合)next.jsを使用してマルチテナントSaaSアプリケーションを構築する(バックエンド統合)Apr 11, 2025 am 08:23 AM

私はあなたの日常的な技術ツールを使用して機能的なマルチテナントSaaSアプリケーション(EDTECHアプリ)を作成しましたが、あなたは同じことをすることができます。 まず、マルチテナントSaaSアプリケーションとは何ですか? マルチテナントSaaSアプリケーションを使用すると、Singの複数の顧客にサービスを提供できます

next.jsを使用してマルチテナントSaaSアプリケーションを構築する方法(フロントエンド統合)next.jsを使用してマルチテナントSaaSアプリケーションを構築する方法(フロントエンド統合)Apr 11, 2025 am 08:22 AM

この記事では、許可によって保護されたバックエンドとのフロントエンド統合を示し、next.jsを使用して機能的なedtech SaaSアプリケーションを構築します。 FrontEndはユーザーのアクセス許可を取得してUIの可視性を制御し、APIリクエストがロールベースに付着することを保証します

JavaScript:Web言語の汎用性の調査JavaScript:Web言語の汎用性の調査Apr 11, 2025 am 12:01 AM

JavaScriptは、現代のWeb開発のコア言語であり、その多様性と柔軟性に広く使用されています。 1)フロントエンド開発:DOM操作と最新のフレームワーク(React、Vue.JS、Angularなど)を通じて、動的なWebページとシングルページアプリケーションを構築します。 2)サーバー側の開発:node.jsは、非ブロッキングI/Oモデルを使用して、高い並行性とリアルタイムアプリケーションを処理します。 3)モバイルおよびデスクトップアプリケーション開発:クロスプラットフォーム開発は、反応および電子を通じて実現され、開発効率を向上させます。

See all articles

ホット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衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

MantisBT

MantisBT

Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

VSCode Windows 64 ビットのダウンロード

VSCode Windows 64 ビットのダウンロード

Microsoft によって発売された無料で強力な IDE エディター

SublimeText3 英語版

SublimeText3 英語版

推奨: Win バージョン、コードプロンプトをサポート!

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強力な PHP 統合開発環境