Maison  >  Article  >  interface Web  >  Parlons du currying des fonctions JavaScript

Parlons du currying des fonctions JavaScript

WBOY
WBOYavant
2022-03-04 18:16:101986parcourir

Cet article vous apporte des connaissances pertinentes sur javascript, qui présente principalement les problèmes liés au currying de fonctions en JavaScript. Le currying consiste à transformer une fonction qui accepte plusieurs paramètres en une fonction qui accepte un seul paramètre et renvoie une nouvelle fonction qui. accepte les paramètres restants et renvoie le résultat. J'espère que cela sera utile à tout le monde.

Parlons du currying des fonctions JavaScript

Recommandations associées : Tutoriel javascript

1. Comprenez brièvement apply et call

  • call et apply existent tous les deux pour changer le contexte lorsqu'une certaine fonction est en cours d'exécution. En d'autres termes, afin de changer le. pointeur de ceci à l’intérieur du corps de la fonction.
  • call et apply ont exactement la même fonction, mais la façon dont ils acceptent les paramètres est différente. call est en fait une sorte de sucre syntaxique à appliquer.
  • Format : apply(context,[arguments]), call(context,param1,param2,...). apply(context,[arguments]),call(context,param1,param2,...)

二、什么是函数柯里化?

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

在这里举个例子,有一个add()函数,它是用来处理我们传给它的参数(param1,params2,…)相加求和的一个函数。

// 在这里第一个具有两个参数`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;
}

如果我们将add()函数柯里化,是什么样子呢?在这里简单的实现一下:

// 柯里化过的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

从以上简单柯里化的add()函数可以看出,函数可以接受部分函数,然后返回一个新的函数,使其继续处理剩下的函数。

三、写一个公共的柯里化函数

在这里我们创建一个公共的柯里化函数,那样我们就不必每次写一个函数都要在其内部实现复杂的柯里化过程。

// 定义一个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,并不是一个真的数组,只是一个具有length属性的对象,所以我们从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

当然这里并不局限于两个参数的柯里化,也可以多个参数:

// 多个参数的普通函数
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

通过以上的例子,我们可以发现一个局限,那就是不管是两个参数还是多个参数,它只能分两步执行,如以下公式:

  • 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) || …;

我们该怎么实现呢?

四、创建一个灵活的柯里化函数

经过以上练习,我们发现我们创建的柯里化函数存在一定局限性,我们希望函数可以分为多步执行:

// 创建一个可以多步执行的柯里化函数,当参数满足数量时就去执行它:
// 函数公式: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

以上我们已经实现了灵活的柯里化函数,但是这里我们又发现了一个问题:

  • 如果我第一次就把参数全部传入,但是它并没有返回结果,而是一个函数(function)。
  • 只有我们再次将返回的函数调用一次才能返回结果:curryAdd(add,1,2,3,4)();
  • 可能有人说如果是全部传参,就调用原来的add()
2. Qu'est-ce que le curry fonctionnel ?

Le currying est la technique consistant à transformer une fonction qui accepte plusieurs paramètres en une fonction qui accepte un seul paramètre (le premier paramètre de la fonction d'origine), et renvoie une nouvelle fonction qui accepte les paramètres restants et renvoie le résultat.

A titre d'exemple ici, il existe une fonction add(), qui est une fonction utilisée pour traiter l'addition et la somme des paramètres (param1, params2,...) que nous lui passons.

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);
		}
	}}

À quoi cela ressemblerait-il si nous exécutions la fonction add() ? Voici une implémentation simple :

// 当参数满足,再次执行时调用函数
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

Comme le montre la simple fonction curry add() ci-dessus, la fonction peut accepter certaines fonctions puis renvoyer une nouvelle fonction afin qu'elle puisse continuer à traiter le fonctions restantes ci-dessous. 🎜🎜3. Écrivez une fonction de curry public🎜🎜Ici, nous créons une fonction de curry public, afin que nous n'ayons pas à implémenter le processus de curry complexe à l'intérieur à chaque fois que nous écrivons une fonction. 🎜rrreee🎜Dans la fonction publique curry ci-dessus : 🎜🎜🎜arguments, n'est pas un vrai tableau, juste un objet avec l'attribut length, donc on part de Array.prototype emprunte la méthode slice pour nous aider à convertir les arguments en un véritable tableau afin de faciliter nos meilleures opérations. 🎜🎜Lorsque nous appelons la fonction createCurry pour la première fois, la variable stored_args contient les paramètres sauf le premier paramètre, car le premier paramètre est ce dont nous avons besoin des fonctions Curried. 🎜🎜Lorsque nous exécutons la fonction renvoyée dans la fonction createCurry, la variable new_args récupère les paramètres et les convertit en tableau. 🎜🎜La fonction renvoyée accède en interne à la valeur stockée dans la variable stored_args via la fermeture et fusionne la valeur de la variable new_args dans un nouveau tableau et l'assigne à la variable < code>arguments . 🎜🎜Enfin, appelez la méthode fn.apply(null,args) pour exécuter la fonction curry. 🎜🎜🎜Testons maintenant la fonction de curry public🎜rrreee🎜Bien sûr, cela ne se limite pas au curry de deux paramètres, mais peut également avoir plusieurs paramètres : 🎜rrreee🎜Grâce à l'exemple ci-dessus, nous pouvons trouver une limitation, c'est-à-dire qu'il s'agisse de deux paramètres ou de plusieurs paramètres, elle ne peut être exécutée qu'en deux étapes, comme la formule suivante : 🎜🎜🎜fn(x,y) ==> );🎜🎜fn(x,y,z,w) ==> fn(x)(y,z,w) || fn(x,y)(z,w)||…🎜🎜🎜Si nous Vous voulez être plus flexible : 🎜🎜🎜fn(x,y) ==> fn(x)(y);🎜🎜fn(x,y,z) ==> | fn(x)(y)(z);🎜🎜fn(x,y,z,w) ==> ( z)(w) || …;🎜🎜🎜Comment le mettre en œuvre ? 🎜🎜4. Créer une fonction de curry flexible🎜🎜Après les exercices ci-dessus, nous avons constaté que la fonction de curry que nous avons créée présente certaines limites. Nous espérons que la fonction pourra être exécutée en plusieurs étapes : 🎜rrreee🎜Nous avons atteint le curry flexible ci-dessus. fonction, mais ici on retrouve un autre problème : 🎜🎜🎜Si je passe tous les paramètres pour la première fois, il ne renvoie pas un résultat, mais une fonction. 🎜🎜Le résultat ne peut être renvoyé que si nous appelons à nouveau la fonction renvoyée : curryAdd(add,1,2,3,4)();🎜🎜Certaines personnes peuvent dire que si tous les paramètres sont passé, appelez simplement La fonction add() d'origine suffira, et c'est aussi une méthode, mais comme nous rencontrons le nombre de paramètres ici, nous sommes toujours confrontés à cette situation ; 🎜🎜🎜Ici, il suffit de porter un jugement avant de renvoyer la fonction : 🎜rrreee🎜Ce qui précède peut être considéré comme complétant une fonction curry flexible, mais ce n'est pas très flexible ici car nous ne pouvons pas la contrôler Une fois exécutée, elle le sera. exécuté automatiquement tant que le nombre de paramètres est suffisant. Que devons-nous faire si nous voulons mettre en place un timing capable de contrôler son exécution ? 🎜🎜5. Écrivez une fonction au curry avec un temps d'exécution contrôlable🎜🎜Expliquons directement la formule de la fonction ici :🎜
  • 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学习教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer