関連する推奨事項: JavaScript チュートリアル
apply(context,[arguments])
、 call(context,param1,param2,...)
。カリー化は、複数のパラメーターを受け入れる関数を 1 つのパラメーター (元の関数の最初のパラメーター) を受け入れる関数に変換し、残りのパラメーターを受け入れて結果を返す新しい関数を返す手法です。
たとえば、ここにadd()
関数があります。これは、渡すパラメータ (param1、params2、...) の加算と合計を処理するために使用される関数です。
// これは 2 つのパラメータを持つ最初の `add(x, y)` 関数です `x`, `y` function add(x, y){ x + y を返します。 } // `add()` 関数を呼び出し、2 つのパラメータ `4` と `6` を与えます 追加(4,6); // コンピューター操作をシミュレートし、最初のステップで最初のパラメーター 4 を渡します 関数 add(4, y){ 4 + y を返します。 } // コンピューター操作をシミュレートし、2 番目のステップで最初のパラメーター 6 を渡します 関数 add(4, 6){ 4 + 6 を返します。
add()
関数をカリー化するとどうなるでしょうか
?
以下に簡単な実装を示します。
// カリー化された add() 関数はいくつかのパラメータを受け入れることができます function add(x,y){ if (typeof y === '未定義') { 戻り関数 (newy){ x + newy を返します。 } } // アプリケーション全体が x + y を返します。 } // テストは console.log(typeof add(4)) を呼び出します // [関数] console.log(add(4)(6)); // 10 // 保存関数を作成できます let saveAdd = add(4); console.log(saveAdd(6)); // 10
上記の単純なカリー化されたadd()
関数からわかるように、この関数はいくつかの関数を受け入れ、新しい関数を返して残りの関数の処理を続行できます。
ここではパブリック カリー化関数を作成します。これにより、関数を作成するたびにその内部に複雑なカリー化プロセスを実装する必要がなくなります。
//createCurry 関数を定義する function createCurry(fn){ var スライス = Array.prototype.slice, storage_args = スライス.コール(引数,1); 戻り関数 () { new_args = スライス.コール(引数) にします。 args = storage_args.concat(new_args); fn.apply(null,args) を返します。
上記のパブリック カリー化関数では
、
arguments
実数の配列ではなく、 length
属性を持つオブジェクトであるため、より適切な操作を容易にするために、 Array.prototype
からslice
メソッドを借用してarguments
を実数の配列に変換します。createCurry
呼び出すと、最初のパラメータがカリー化する必要がある関数であるため、変数stored_args
は最初のパラメータ以外のパラメータが保持されます。createCurry
関数で返された関数を実行すると、変数new_args
パラメータを取得し、配列に変換します。stored_args
に格納されている値にアクセスし、変数new_args
の値を新しい配列にマージして、変数args
に割り当てます。fn.apply(null,args)
メソッドを呼び出して、カリー化された関数を実行します。次に、パブリックのカリー化された関数
// 通常の関数 add() をテストしてみましょう
関数 add(x, y){ x + y を返します。 } // 新しい関数を取得するためのカリー var newAdd = createCurry(add,4); console.log(newAdd(6)); // 10 //もう 1 つの簡単な方法 console.log(createCurry(add,4)(6));// 10
もちろん、これは 2 つのパラメータのカリー化に限定されず、複数のパラメータも対象となります。
// 複数のパラメータ 通常の関数 function add (a、b、c、d){ a + b + c + dを返します。 } // 関数をカリーして新しい関数を取得します。複数のパラメータを自由に分割できます console.log(createCurry(add,4,5)(5,6)); // 20 // 二段階カレー let add_one = createCurry(add,5); console.log(add_one(5,5,5));// 20 add_two = createCurry(add_one,4,6); console.log(add_two(6)); // 21
上記の例から、パラメータが 2 つであっても複数のパラメータであっても、次の式のように 2 つのステップでしか実行できないという制限があることがわかります。
より柔軟にしたい場合:
どのように実装すればよいでしょうか?
上記の演習を行った結果、作成したカリー化関数には特定の制限があることがわかりました。この関数を複数のステップで実行できるようにしたいと考えています。
// 複数のステップで実行できるカリー化関数を作成します。 、パラメータの数が満たされたときに実行します。 // 関数式: fn(x,y,z,w) ==> fn(x)(y)(z)(w); let createCurry = (fn,...params)=> { args = parsms || []; とします。 let fnLen = fn.length; // カリー化された関数のパラメータの長さを指定します。 return (...res)=> { // スコープ チェーンを通じて以前のパラメータをすべて取得します let allArgs = args.slice(0); // 後続の操作の影響を避けるために、クロージャによって共有される args パラメータをディープ コピーします (参照型) allArgs.push(...res); if(allArgs.length < fnLen){ // パラメータの数が元の関数のパラメータ長より小さい場合、createCurry 関数を再帰的に呼び出します return createCurry.call(this,fn,...allArgs); }それ以外{ // パラメータの数が満たされた場合、トリガー関数の実行 return fn.apply(this,allArgs); } } } // 複数のパラメータを持つ通常の関数 function add(a,b,c,d){ a + b + c + dを返します。 } // カリー化関数をテストします letcurryAdd = createCurry(add,1); console.log(curryAdd(2)(3)(4)); //
私たちは 10 年以上にわたって柔軟なカリー化関数を実装してきましたが、ここで別の問題が見つかりました
curryAdd(add,1,2,3,4)()
;add()
関数を呼び出すだけでよいと言う人もいるかもしれません。これも一方向ですが、ここではパラメータの数を満たしているため、この状況に対処します。ここでは、関数を返す前に判断する必要があるだけです。
let createCurry = (fn,...params)=> { args = parsms || []; とします。 let fnLen = fn.length; // カリー化された関数のパラメータの長さを指定します。 if(長さ === _args.length){ //判定を追加する。初回のパラメータの数が十分であれば、関数を直接呼び出して結果を取得する return fn.apply(this,args); } return (...res)=> { allArgs = args.slice(0); とします。 allArgs.push(...res); if(allArgs.length < fnLen){ return createCurry.call(this,fn,...allArgs); }それ以外{ fn.apply(this,allArgs) を返します。 } 上記
は柔軟なカリー化された関数とみなすことができますが、パラメーターの数が十分である限り、実行されるタイミングを制御できないため、ここではあまり柔軟性がありません。実行を制御できるタイミングを実現したい場合はどうすればよいでしょうか?
ここで関数の式を直接説明します。
// パラメータが満たされたら、関数を呼び出します let createCurry = (fn,...params)=> { args = parsms || []; とします。 let fnLen = fn.length; // カリー化された関数のパラメータの長さを指定します。 //もちろん、ここでの判断はコメントアウトする必要があります。コメントアウトしないと、初めてパラメータの数が揃ったときに結果が直接実行されます。 //if(length === _args.length){ // 判定を追加。最初のパラメータの数が十分であれば、関数を直接呼び出して結果を取得します。 //return fn.apply(this,args); //} return (...res)=> { allArgs = args.slice(0); とします。 allArgs.push(...res); // ここでは入力パラメータが0より大きいかどうかを判定します。0より大きい場合はパラメータの数が十分かどうかを判定します。 // ここでは && は使用できません。&& を使用した場合、パラメータの数が十分な場合に結果が実行されます。 if(res.length > 0 || allArgs.length < fnLen){ return createCurry.call(this,fn,...allArgs); }それ以外{ fn.apply(this,allArgs) を返します。 } } } // 複数のパラメータを持つ通常の関数 function add(a,b,c,d){ a + b + c + dを返します。 } // 制御可能なカリー化関数をテストします letcurryAdd = createCurry(add,1); console.log(curryAdd(2)(3)(4)); // 関数 console.log(curryAdd(2)(3)(4)()); // 10 console.log(curryAdd(2)(3)()); // パラメータが足りない場合は NaN を返します。
関連する推奨事項:
上記は JavaScript 関数のカリー化の詳細について説明しています。 php 中国語 Web サイトのその他の関連記事にご注意ください。