ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript 関数のカリー化について話しましょう

JavaScript 関数のカリー化について話しましょう

WBOY
WBOY転載
2022-03-04 18:16:101986ブラウズ

この記事では、javascript に関する関連知識を提供します。主に JavaScript における関数のカリー化に関連する問題を紹介します。カリー化とは、複数のパラメーターを受け入れる関数を変換することです。単一のパラメーターを受け入れる手法です。そして、残りのパラメータを受け入れて結果を返す新しい関数を返します。

JavaScript 関数のカリー化について話しましょう

#関連する推奨事項:

javascript チュートリアル

1. apply と call

## を簡単に理解します。 #call と apply はどちらも関数実行時のコンテキストを変更するために存在し、言い換えれば関数本体内で this のポインタを変更するためのものです。
call と apply はまったく同じ機能を持ちますが、パラメータの受け取り方法が異なります。 call は実際には apply の糖衣構文の一種です。
形式:
    apply(context,[arguments])
  • ,call(context,param1,param2,...)
  • 2.関数カリー化とは何ですか?

カリー化とは、複数のパラメーターを受け入れる関数を 1 つのパラメーター (元の関数の最初のパラメーター) を受け入れる関数に変換し、残りのパラメーターを受け入れて結果を返す新しい関数を返すことです。 。

たとえば、

add()

関数があります。これは、渡すパラメータ (param1、params2、...) の加算と合計を処理するために使用される関数です。 . . <pre class="brush:php;toolbar:false">// 在这里第一个具有两个参数`x`、`y`的`add(x , y)`函数 function add(x , y){ return x + y; } // 调用`add()`函数,并给定两个参数`4`和`6` add(4,6); // 模拟计算机操作,第一步 传入第一个参数 4 function add(4 , y){ return 4 + y; } // 模拟计算机操作,第二步 传入第一个参数 6 function add(4 , 6){ return 4 + 6; }</pre>

add()

関数をカリー化するとどうなるでしょうか?これは簡単な実装です: <pre class="brush:php;toolbar:false">// 柯里化过的add()函数,可以接受部分参数 function add(x ,y){ if (typeof y === 'undefined') { return function (newy){ return x + newy; } } // 完整应用 return x + y; } // 测试调用 console.log(typeof add(4)); // [Function] console.log(add(4)(6)); // 10 // 可以创建保存函数 let saveAdd = add(4); console.log(saveAdd(6)); // 10</pre>上記の単純なカリー化された

add()

関数からわかるように、この関数はいくつかの関数を受け入れてから、新しい関数を返して続行させることができます。残りの機能。 3. パブリック カリー化関数を作成する

ここでは、関数を記述するたびに複雑な関数を内部で実装する必要がないように、パブリック カリー化関数を作成します。

// 定义一个createCurry的函数function createCurry(fn){
	var slice = Array.prototype.slice,
	stored_args = slice.call(arguments,1);
	
	return function () {
		let new_args = slice.call(arguments),
		args = stored_args.concat(new_args);
		return fn.apply(null,args);
	}}

上記のパブリック カリー化関数:

    arguments
  • は実際の配列ではなく、lengthAttribute オブジェクトを持つ単なる配列ですそのため、Array.prototype から slice メソッドを借用して、arguments を実際の配列に変換し、より適切な操作を容易にします。 関数
  • createCurry
  • を初めて呼び出すと、変数 stored_args は最初のパラメーターを除くパラメーターを保持します。これは、最初のパラメーターが「カリー化された関数が必要」であるためです。 関数
  • createCurry
  • で返された関数を実行すると、変数 new_args がパラメーターを取得し、配列に変換します。 返された関数は内部的にクロージャを通じて変数
  • stored_args
  • に格納された値にアクセスし、変数 new_args の値は新しい配列に結合されて、変数 args最後に
  • fn.apply(null,args)
  • メソッドを呼び出して、カリー化された関数を実行します。
次に、パブリック カリー化関数をテストしましょう

// 普通函数add()
function add(x , y){
	return x + y;
}

// 柯里化得到一个新的函数
var newAdd = createCurry(add,4);
console.log(newAdd(6)); // 10


//另一种简便方式
console.log(createCurry(add,4)(6));// 10
もちろん、これは 2 つのパラメーターのカリー化に限定されず、複数のパラメーターをカリー化することもできます。

// 多个参数的普通函数
function add(a,b,c,d){
	return 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
let add_two = createCurry(add_one,4,6);
console.log(add_two(6)); // 21

上記の例を通じて、制限がわかります。つまり、2 つのパラメーターであっても複数のパラメーターであっても、次の式のような 2 つのステップでのみ実行できます。

fn(x,y) ==> fn(x)(y);

fn(x,y,z,w) ==> fn(x)(y,z, w) || fn(x,y)(z,w)||…
  • もっと柔軟にしたい場合:
fn(x,y) = => fn(x)(y);

fn(x,y,z) ==> fn(x,y)(z) || fn(x)(y)(z ) ;
  • fn(x,y,z,w) ==> fn(x,y)(z)(w) || fn(x)(y)(z)(w) | | …;
  • どうすればそれを達成できますか?
  • 4. 柔軟なカリー化関数の作成

上記の演習の後、作成したカリー化関数には特定の制限があることがわかりました。関数が複数のステップで実行できることを期待します:

// 创建一个可以多步执行的柯里化函数,当参数满足数量时就去执行它:
// 函数公式:fn(x,y,z,w) ==> fn(x)(y)(z)(w);
let createCurry = (fn,...params)=> {
	let 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);
		}else{
		  // 当参数数量满足时,触发函数执行
		  return fn.apply(this,allArgs);
		}
	}
}


// 多个参数的普通函数
function add(a,b,c,d){
	return a + b + c + d;
}

// 测试柯里化函数

let curryAdd = createCurry(add,1);
console.log(curryAdd(2)(3)(4)); // 10

上記で柔軟なカリー化関数を実装しましたが、ここで別の問題が見つかりました:

初めてすべてのパラメーターを渡しても結果が返されない場合、しかし関数です。

返された関数をもう一度呼び出した場合にのみ、結果を返すことができます:
    curryAdd(add,1,2,3,4)()
  • ;
  • 一部の人「すべてのパラメータを渡すには、元の add() 関数を呼び出すだけです。これも 1 つの方法です。しかし、パラメータの数を満たすためにここにいるので、この状況に対処します。」
  • ここで必要なのは、関数を返す前に判断することだけです:
    let createCurry = (fn,...params)=> {
    	let args = parsms || [];
    	let fnLen = fn.length; // 指定柯里化函数的参数长度
    	
    	if(length === _args.length){
    	   // 加入判断,如果第一次参数数量以经足够时就直接调用函数获取结果
               return fn.apply(this,args);
            }
    	return (...res)=> {
    		let allArgs = args.slice(0);
    		allArgs.push(...res);
    		if(allArgs.length < fnLen){
    		   return createCurry.call(this,fn,...allArgs);
    		}else{
    		  return fn.apply(this,allArgs);
    		}
    	}}
  • 上記は柔軟なカリー化関数を完成したとみなすことができますが、まだ完成していません。計算は非常に複雑です。いつ実行するかを制御できないため柔軟性があり、パラメータの数が十分である限り自動的に実行されます。実行を制御できるタイミングを実現したい場合はどうすればよいでしょうか?

5. 実行時間を制御できるカリー化関数を作成する

ここで関数の式を直接説明しましょう:

  • fn(a,b,c) ==> fn(a)(b)(c )();
  • fn(a,b,c) ==> fn(a);fn(b);fn(c );fn();
  • 当我们参数足够时它并不会执行,只有我们再次调用一次函数它才会执行并返回结果。在这里我们在以上例子中加一个小小的条件就可以实现。
  • // 当参数满足,再次执行时调用函数
    let createCurry = (fn,...params)=> {
    	let args = parsms || [];
    	let fnLen = fn.length; // 指定柯里化函数的参数长度
    	
    	//当然这里的判断需要注释掉,不然当它第一次参数数量足够时就直接执行结果了
    	//if(length === _args.length){
    	   // 加入判断,如果第一次参数数量以经足够时就直接调用函数获取结果
               //return fn.apply(this,args);
            //}
    	return (...res)=> {
    		let allArgs = args.slice(0);
    		allArgs.push(...res);
    		// 在这里判断输入的参数是否大于0,如果大于0在判断参数数量是否足够,
    		// 这里不能用 && ,如果用&& 也是参数数量足够时就执行结果了。
    		if(res.length > 0 || allArgs.length < fnLen){
    		   return createCurry.call(this,fn,...allArgs);
    		}else{
    		  return fn.apply(this,allArgs);
    		}
    	}
    }
    
    
    // 多个参数的普通函数
    function add(a,b,c,d){
    	return a + b + c + d;
    }
    
    // 测试可控制的柯里化函数
    
    let curryAdd = createCurry(add,1);
    console.log(curryAdd(2)(3)(4)); // function
    console.log(curryAdd(2)(3)(4)()); // 10
    console.log(curryAdd(2)(3)()); // 当参数不足够时返回 NaN

    相关推荐:javascript学习教程

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

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。