ホームページ > 記事 > ウェブフロントエンド > JavaScript における関数カリー化の詳細な分析_javascript スキル
カレーの由来は数学者のハスケル・カリーの名前です(プログラミング言語のハスケルも彼の名前にちなんでいます)。
カリー化は通常、部分評価とも呼ばれます。これは、関数にパラメータを段階的に渡し、各パスの後にパラメータを部分的に適用し、残りのパラメータを受け入れるためにより具体的な関数を返すことを意味します。これは、次のように複数のレベルでネストできます。関数は受け入れます。最終結果が返されるまで、いくつかのパラメータが必要になります。
したがって、カリー化のプロセスは、徐々にパラメータを渡し、関数の適用範囲を徐々に狭め、徐々に問題を解決するプロセスです。
合計関数の実行
段階的な評価に従って、簡単な例を見てみましょう
var concat3Words = function (a, b, c) { return a+b+c; }; var concat3WordsCurrying = function(a) { return function (b) { return function (c) { return a+b+c; }; }; }; console.log(concat3Words("foo ","bar ","baza")); // foo bar baza console.log(concat3WordsCurrying("foo ")); // [Function] console.log(concat3WordsCurrying("foo ")("bar ")("baza")); // foo bar baza
ご覧のとおり、concat3WordsCurrying("foo ") は関数です。各呼び出しは新しい関数を返し、その関数が別の呼び出しを受け入れて、最終結果が返されるまで新しい関数を返します。段階的に分散ソリューションが実行されます。 (追記: ここではクロージャの特性が使われています)
それでは、さらに一歩進んで、3 つ以上のパラメーターを渡す必要がある場合、パラメーターが渡されない場合は、必要な数のパラメーターを渡すことができます。
まず、一般的な実装を行ってみましょう:
var add = function(items){ return items.reduce(function(a,b){ return a+b }); }; console.log(add([1,2,3,4]));
しかし、各数値を 10 で乗算してから合計する必要がある場合は、次のようになります。
var add = function (items,multi) { return items.map(function (item) { return item*multi; }).reduce(function (a, b) { return a + b }); }; console.log(add([1, 2, 3, 4],10));
幸いなことに、map 関数とreduce 関数があり、このモデルに従い、各項目に 1 を加算して集計する場合は、map 内の関数を置き換える必要があります。
カリー化の実装を見てみましょう:
var adder = function () { var _args = []; return function () { if (arguments.length === 0) { return _args.reduce(function (a, b) { return a + b; }); } [].push.apply(_args, [].slice.call(arguments)); return arguments.callee; } }; var sum = adder(); console.log(sum); // Function sum(100,200)(300); // 调用形式灵活,一次调用可输入一个或者多个参数,并且支持链式调用 sum(400); console.log(sum()); // 1000 (加总计算)
上記の加算器はカリー化された関数であり、新しい関数を返します。新しい関数はバッチで新しいパラメーターを受け取り、計算を最後まで遅らせることができます。
ユニバーサルカレー機能
より一般的なカリー化では、最後の計算を関数にカプセル化し、この関数をパラメータとしてカリー化関数に渡します。これは明確で柔軟です。
たとえば、各項目を 10 倍する場合、処理関数をパラメータとして渡すことができます。
var currying = function (fn) { var _args = []; return function () { if (arguments.length === 0) { return fn.apply(this, _args); } Array.prototype.push.apply(_args, [].slice.call(arguments)); return arguments.callee; } }; var multi=function () { var total = 0; for (var i = 0, c; c = arguments[i++];) { total += c; } return total; }; var sum = currying(multi); sum(100,200)(300); sum(400); console.log(sum()); // 1000 (空白调用时才真正计算)
このように、sum =curerying(multi) の呼び出しは非常に明確であり、使用効果も素晴らしく、たとえば、複数の値を蓄積したい場合は、複数の値をパラメータとして使用できます sum( 1,2,3)、連鎖呼び出し、sum(1)(2)(3)
もサポート可能
カレー作りの基本
上記のコードは実際には高階関数であり、1 つ以上の関数をパラメーターとして受け取り、新しい関数を返します。さらに、中間プロセスで入力されたパラメータを保存するためにクロージャの特性にも依存します。つまり:
関数はパラメータとして渡すことができます
関数は関数の戻り値として使用できます
閉店
カレーの役割
計算が遅れています。上の例は比較的簡単に説明できます。
パラメータの再利用。同じ関数が複数回呼び出され、渡されるパラメータがほとんど同じである場合、その関数はカリー化の適切な候補となる可能性があります。
関数を動的に作成します。これは、部分的に結果を計算した後、後続の業務を処理するための新しい関数を動的に生成することで実行できるため、計算の繰り返しが不要になります。または、呼び出し側関数に渡されるパラメーターのサブセットを部分的に関数に適用することで、新しい関数を動的に作成することもできます。この新しい関数は、繰り返し渡されるパラメーターを保存します (毎回パラメーターを渡す必要はありません)。将来)。たとえば、イベント ブラウザはイベントのヘルパー メソッドを追加します。
var addEvent = function(el, type, fn, capture) { if (window.addEventListener) { el.addEventListener(type, function(e) { fn.call(el, e); }, capture); } else if (window.attachEvent) { el.attachEvent("on" + type, function(e) { fn.call(el, e); }); } };
イベント処理を追加するたびに if...else.... を実行する必要がありますが、実際にはブラウザ上で必要な判定は 1 つだけであり、その 1 つの判定結果に基づいて新しい関数が動的に生成されます。将来的には再計算する必要はありません。
var addEvent = (function(){ if (window.addEventListener) { return function(el, sType, fn, capture) { el.addEventListener(sType, function(e) { fn.call(el, e); }, (capture)); }; } else if (window.attachEvent) { return function(el, sType, fn, capture) { el.attachEvent("on" + sType, function(e) { fn.call(el, e); }); }; } })();
この例では、最初の if...else... の判断の後、計算の一部が完了し、後で渡されるパラメーターを処理するための新しい関数が動的に作成されます。これは典型的なカリー化です。
Function.prototype.bind メソッドもカレー化されたアプリケーション
call/apply メソッドの直接実行とは異なり、bind メソッドは最初のパラメーターを関数実行のコンテキストとして設定し、他のパラメーターが呼び出し側メソッドに順番に渡されます (関数自体の本体は実行され、遅延実行と見なすことができます)、動的作成は、カリー化の特性に準拠した新しい関数を返します。
var foo = {x: 888}; var bar = function () { console.log(this.x); }.bind(foo); // 绑定 bar(); // 888
以下はバインド関数のシミュレーションです。testBind は新しい関数を作成して返します。新しい関数では、実際に業務を実行する関数が実パラメータとして渡されたコンテキストにバインドされ、実行が遅延されます。 。
Function.prototype.testBind = function (scope) { var fn = this; //// this 指向的是调用 testBind 方法的一个函数, return function () { return fn.apply(scope); } }; var testBindBar = bar.testBind(foo); // 绑定 foo,延迟执行 console.log(testBindBar); // Function (可见,bind之后返回的是一个延迟执行的新函数) testBindBar(); // 888
ここで、プロトタイプにおけるこれの理解に注意する必要があります。
上記の記事は、JavaScript における関数カリー化の詳細な分析です。これは、編集者が共有したすべての内容です。参考にしていただければ幸いです。Script Home をサポートしていただければ幸いです。