>웹 프론트엔드 >JS 튜토리얼 >JavaScript 함수 커링에 대해 이야기해 보겠습니다.

JavaScript 함수 커링에 대해 이야기해 보겠습니다.

WBOY
WBOY앞으로
2022-03-04 18:16:102043검색

이 글에서는 JavaScript의 함수 커링과 관련된 문제를 주로 소개하는 javascript에 대한 관련 지식을 제공합니다. 커링은 여러 매개변수를 허용하는 함수를 단일 매개변수를 허용하는 함수로 변환하고 이를 반환하는 새로운 함수입니다. 나머지 매개변수를 수락하고 결과를 반환합니다. 이것이 모든 사람에게 도움이 되기를 바랍니다.

JavaScript 함수 커링에 대해 이야기해 보겠습니다.

관련 추천: javascript tutorial

1. Apply와 call

  • call을 간단히 이해하고, Apply와 Apply 둘 다 존재하면 특정 기능이 실행될 때 컨텍스트를 변경합니다. 함수 본문 내부의 this 포인터입니다.
  • call과 Apply는 기능은 완전히 동일하지만 매개변수를 받아들이는 방식이 다릅니다. call은 실제로 적용을 위한 일종의 구문 설탕입니다.
  • 형식: apply(context,[인수]), 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. 함수 커링이란?

Currying은 여러 매개변수를 받는 함수를 단일 매개변수(원래 함수의 첫 번째 매개변수)를 받는 함수로 변환하고, 나머지 매개변수를 받아 결과를 반환하는 새로운 함수를 반환하는 기술입니다.

여기의 예로 add() 함수가 있는데, 이는 우리가 전달하는 매개변수(param1, params2,...)의 더하기와 합을 처리하는 데 사용되는 함수입니다.

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

add() 함수를 커리하면 어떤 모습일까요? 다음은 간단한 구현입니다.

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

위의 간단한 카레 add() 함수에서 볼 수 있듯이 이 함수는 일부 함수를 수락한 다음 새 함수를 반환하여 해당 작업을 계속 처리할 수 있습니다. 나머지 기능은 아래에 있습니다. 🎜🎜3. 공개 커링 함수 작성🎜🎜여기에서는 함수를 작성할 때마다 내부에 복잡한 커링 프로세스를 구현할 필요가 없도록 공개 커링 함수를 만듭니다. 🎜rrreee🎜위의 공개 카레 함수에서: 🎜🎜🎜arguments는 실제 배열이 아니고 단지 length 속성이 있는 객체이므로 부터 시작합니다. Array.prototypeslice 메소드를 빌려 인수를 실제 배열로 변환하여 더 나은 작업을 수행할 수 있도록 도와줍니다. 🎜🎜 createCurry 함수를 처음 호출할 때 stored_args 변수는 첫 번째 매개변수를 제외한 매개변수를 보유합니다. 첫 번째 매개변수가 Curried 함수에 필요한 것이기 때문입니다. 🎜🎜 createCurry 함수에서 반환된 함수를 실행하면 new_args 변수가 매개변수를 가져와서 배열로 변환합니다. 🎜🎜반환된 함수는 클로저를 통해 내부적으로 stored_args 변수에 저장된 값에 액세스하고 new_args 변수의 값을 새 배열로 병합하여 < 코드>인수. 🎜🎜마지막으로 fn.apply(null,args) 메서드를 호출하여 카레 함수를 실행합니다. 🎜🎜🎜이제 공개 커링 기능을 테스트해 보겠습니다.🎜rrreee🎜물론 이는 두 매개변수의 커링에 국한되지 않고 여러 매개변수를 가질 수도 있습니다. 🎜rrreee🎜위의 예를 통해 다음을 수행할 수 있습니다. 즉, 매개변수가 두 개이든 여러 개이든 상관없이 다음 공식과 같이 두 단계로만 실행할 수 있습니다. 🎜🎜🎜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,w) ==>fn(x,y)(z)(w) || ( z)(w) || …;🎜🎜🎜어떻게 구현하나요? 🎜🎜4. 유연한 카레 함수 만들기🎜🎜위의 연습을 통해 우리가 만든 카레 함수에 특정 제한이 있음을 발견했습니다. 우리는 이 함수가 여러 단계로 실행될 수 있기를 바랍니다. 🎜rrreee🎜우리는 위의 유연한 카레를 달성했습니다. 함수인데 여기서 또 다른 문제를 발견합니다. 🎜🎜🎜모든 매개변수를 처음으로 전달하면 결과가 반환되지 않고 함수가 반환됩니다. 🎜🎜반환된 함수를 다시 한 번 호출하는 경우에만 결과가 반환될 수 있습니다: curryAdd(add,1,2,3,4)();🎜🎜어떤 사람들은 모든 매개변수가 다음과 같다고 말할 수도 있습니다. 전달되면 그냥 호출하면 됩니다. 원래 add() 함수로 충분하며 이 역시 메서드이지만 여기에서 매개변수 수를 충족하므로 이 상황을 계속 처리합니다. 🎜🎜🎜여기서는 함수를 반환하기 전에 판단만 하면 됩니다. 🎜rrreee🎜위는 유연한 카레 함수를 완성한 것으로 간주할 수 있지만 여기서는 제어할 수 없기 때문에 그다지 유연하지 않습니다. 매개변수 수가 충분하면 자동으로 실행됩니다. 실행을 제어할 수 있는 타이밍을 구현하려면 어떻게 해야 합니까? 🎜🎜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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 csdn.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제