여러개의 인자를 받는 함수를 쪼개서
인자를 하나씩 받고, 다 받았을 때 콜백함수를 실행하는 함수이다.
function foo (add, a, b, c) {
return add(a, b, c);
}
foo(
function add (a, b, c){
return a + b + c;
},
1, 2, 3
); // 6
위와 같은 함수를 커링기법을 이용해서 쪼갤 수 있다는 것.
⏸
function curry (func) {
return function(a) {
return function(b) {
return function(c) {
return func(a, b, c);
}
}
}
}
리턴의 리턴을 거듭하는 위와 같은 형태이다.
클로저로 인해 가장 내부에서도 a, b, c를 기억해서 받아 온다.
이 함수를 실행해보자
functon add(a, b, c){ return a + b + c; }
foo = curry(add); // add함수를 먼저 받는다.
foo1 = foo(1);
foo2 = foo1(2);
foo3 = foo2(3)
a, b, c 인자의 개수 3개 만큼 foo함수 역시 3번 생성되었다.
여기서 인자개수 = 생성된 함수개수 같을때 결과가 발생하는 것을 알 수 있다.
⏸ 다음과 같이 표현할 수도 있다.
curry(foo)(1)(2)(3);
//or
const curry = add => a => b => c => add(a, b, c);
We can reuse a piece of code give it a partial parameter and create functions that are extensible.
this를 바인딩할 때 주로 사용했던 bind도 이와 유사한 기능을 한다.
인자를 미리 일부만 넘겨주고 필요할때 추가 인자를 주면서 실행할 수 있다.
function multiply(a, b) {
return a * b;
}
const multiplyByTwo = multiply.bind(this, 2);
console.log(multiplyByTwo(4)); // 8
그런데 인자가 몇 개가 될지 모르니 일일히 작성하는데는 한계가 있다.
현업에서는 lodash등의 라이브러리를 사용하겠지만,
어떻게 실행되는 지 lodash를 구현하면서
커리를 먹어🍛..아니.. 이해하려고 한다! 😁
function curry(func) {
// 가장 먼저 받는 func 콜백함수가 curry의 매개변수가 된다.
// 커리는 함수를 리턴해야 하는 함수이니, 우리는 함수를 뱉어야 한다.
return function (arg) {
// 위 curry의 모양을 생각해보자.
// 여기에는 a인자 하나만 들어오게 된다.
// 그런데 추후 계속 함수는 리턴 즉 생성될거고
// 그때마다 인자가 있다면 생기는 함수의 인자로 또 들어올 것이다.
// 그렇다면 우리는 인자를 기억했다가 마지막에 콜백에게 건네야 한다.
// 이 첫번째 함수부터 차곡차곡 기억하게 하려면
// 여기 밖에 변수를 만들어야 한다.
// 그렇다면 다시 세팅해보자.
.
.
.
⏬
함수의 개수가 인자의 개수 이상**일 때
최종적으로 함수가 실행되어 결과를 뱉는다 했었다.
function _curry(func) {
const funcLength = func.length;
// 여기에서 함수의 개수를 미리 확인할 수 있다.
// 함수.length = 함수의 인자 개수 이기 때문!!!!
let args = [];
// 들어올 인자들을 배열에 넣을 것이다.
// const를 안쓰는 이유는 재할당할 에정이라 그렇다.
return function executeFunc(...arg) {
// 재호출을 위해 익명이 아닌 기명함수로 작성해준다.
//...arg로 세팅해주면 arg를 바로 배열로 사용할 수 있다.
// 음 먼저 인자의 개수를 판별할 수 있는 조건문이 있으면 좋겠다.
// 만약 인자가 없다면, 즉 arg가 빈배열이라면
// 함수 혼자 실행해야 한다.
// 인자에 따라서 아래 구문이 없어도 작동할 수도 있다.
if (!arg) {
func();
return;
//or return executeFunc;
}
// 그럼 여기 아래는 arg.length가 있을 때다.
// length가 존재하려면 args에 인자가 들어가야 한다.
args = args.concat(arg);
// concat을 이용해서 인자가 있을경우 추가적으로 생성될 함수의
// 인자를 받아서 args배열에 지속적으로 합쳐질 수 있게
// 위와 같은 코드를 심어준다.
// 허나 여기까지만 작성하고 돌리면 인자가 하나만 담기고
// 그 뒤로는 에러가 뜬다.
// 왜냐면 아직 함수를 호출해서 생성하는 구문이 없기 때문.
// 처음에 언급했던 내용중에
// 인자의 개수와 함수의 개수가 일치할 때 결과가 나온다고 했다.
// 그렇다면 인자의 개수가 함수보다 적다면 추가적으로
// 함수를 생성해야 한다. 즉 리턴해야 한다.
// 다 받아먹는 순간 바로 그때 함수가 최종적으로 실행될 것이다.
// 그럼 그 전까지는 함수를 계속 호출해야 한다.
// 여기서 호출해서 exectuteFunc이 실행되면
// 위에서 args = args.concat(arg);이 실행되어 배열에 추가된다.
if (args.length < funcLength ) {
return executeFunc;
}
// 이 아래는 else 부분이다. 즉
// args.length >= funcLength 일때
// 최종적으로 args의 인자들을 끌어모아서
// 콜백함수를 실행해줘야 한다.
return func.apply(this, args)
// or return func(...args);
}
⏬ final code
function _curry(func) {
const funcLength = func.length;
let args = [];
return function executeFunc(...arg) {
if (!arg) {
return func();
}
args = args.concat(arg);
if (args.length < funcLength ) {
return executeFunc;
}
return func.apply(this, args);
}
}
const fun = (a, b, c) => [a, b, c];
console.log(_curry(fun)(1, 2)(3, 4)); // [1, 2, 3]
const fun2 = (a, b, c, d, e) => a + b + c + d + e;
console.log(_curry(fun2)(1)(2)(3)(4)(5)); // 15