■ Dirty Sample#1
const handler = (e, id) => {
console.log(`${id} 여길 클릭했구나 ${e.offsetX}`);
}
document.addEventListener("click", (e) => handler(e, "front"));
↑↑ addEventListener의 2번째 인자는 콜백함수의 자리다. 보통 위와 같이 코드를 작성하는 경우가 많다.
■ Clean Sample#1
const handler = id => e => {
console.log(`${id} 여길 클릭했구나 ${e.offsetX}`);
}
// 풀어쓰면 아래와 같다.
// const handler = (id) => {
// return (e) => {
// console.log(`${id} 여길 클릭했구나 ${e.offsetX}`);
// }
// }
document.addEventListener("click", handler("front")); // 화살표 함수 생략
handler함수는 2개의 함수가 중첩되어 있는 형태이다. (함수를 반환하는 함수: currying, 익명함수(화살표 함수) + handler 함수 두개를 합친 형태의 함수이다.)
Dirty Sample#1에서 addEventListener의 두 번째 인자의 화살표함수를 생략할 수가 있어 가독성이 첫 번째 보다 비교적 높은 편이다.
■ currying 정의(위키피디아)
In mathematics and computer science, currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a single argument. (아래 코드에서 a 다음 b, 그리고 b 다음 c의 인자를 차례대로 가져올 수 있다.)
const func = (a, b, c) => {
console.log(`${a}, ${b}, ${c}`);
}
const func = (a) => (b) => (c) => {
console.log(`${a}, ${b}, ${c}`);
}
- currying은 함수를 조합해 새로운 함수를 만들 수 있도록 도와준다
■ Currying Sample#1
const cache = (fn) => (a) => (b) => fn(a, b);
const add = (a, b) => a + b;
const result = cache(add)("hello")("world");
console.log(result); // helloworld
■ Currying Sample#2
function curry(f) { // 커링 변환을 하는 curry(f) 함수
return function(a) {
return function(b) {
return f(a, b);
};
};
}
// usage
function sum(a, b) {
return a + b;
}
let curriedSum = curry(sum);
alert( curriedSum(1)(2) ); // 3
■ Currying Sample#3 (advanced)
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}
function sum(a, b, c) {
return a + b + c;
}
let curriedSum = curry(sum);
alert( curriedSum(1, 2, 3) ); // 6, 보통때 처럼 단일 callable 형식으로 호출하기
alert( curriedSum(1)(2,3) ); // 6, 첫 번째 인수를 커링하기
alert( curriedSum(1)(2)(3) ); // 6, 모두 커링하기
객체지향 프로그래밍의 장점 중 하나인 "상속"의 개념을 함수형 프로그래밍에서 사용할 수 있도록 처리해주는 것이 currying이다. 즉, curryign은 중복코드를 재활용해 사용할 수 있게 한다.
function 곱하기(a, b){
return a * b;
}
const 곱하기X = x => a => {
return 곱하기(a, x);
}
// 풀어쓰면 아래와 같다.
// function 곱하기X(x){
// return function(a){
// return 곱하기(a, x);
// }
// }
const 곱하기2 = 곱하기X(2);
const 곱하기3 = 곱하기X(3);
console.log("곱하기2: ", 곱하기2(2)); // 4
console.log("곱하기3: ", 곱하기3(2)); // 6
곱하기X 함수는 배수가될 인자 x를 받는 재활용 가능한 함수이다.
"2x * 3 + 4 = ??"를 currying을 활용해 풀어보자.
// 2x * 3 + 4
const 곱하기 = (a, b) => a * b;
const 곱하기X = x => a => 곱하기(a, x);
const 곱하기2 = 곱하기X(2);
const 곱하기3 = 곱하기X(3);
const 더하기 = (a, b) => a + b;
const 더하기X = x => a => 더하기(x, a);
const 더하기4 = 더하기X(4);
const fomula = (x) => {
return 더하기X(4)(곱하기3(곱하기2(x))); // 함수의 실행방향이 오른쪽에서 왼쪽이다.
}
const result = fomula(10);
console.log(`result: ${result}`); // 64
보다시피 괄호가 너무 많아 가독성이 매우 나쁘다. 그리고 formula 함수의 실행순서(방향)를 보면
"곱하기2 → 곱하기3 → 더하기4"이며 실행방향은 오른쪽에서 왼쪽이다. 보통 사람이 인지하는 순서는 왼쪽에서 오른쪽이다. 그렇기 때문에 코드를 해석하는데 다소 시간이걸리고 혼동될 수 있는 단점이 있다.
■ 해결방법: 배열 API "reduce"를 사용해 실행할 함수들을 조합하는 것이다.
function compose(){
return [fn1, fn2, fn3, ...].reduce(); // enhancedFn 함수들의 조함으로 새로운 함수를 만들었다.
}
위와 같이 함수배열을 원하는 계산 함수(enhancedFn)로 변환하는 과정을 구현하면된다.
// 2x * 3 + 4
const 곱하기 = (a, b) => a * b;
const 곱하기X = x => a => 곱하기(a, x);
const 곱하기2 = 곱하기X(2);
const 곱하기3 = 곱하기X(3);
const 더하기 = (a, b) => a + b;
const 더하기X = x => a => 더하기(x, a);
const 더하기4 = 더하기X(4);
const fomula = [
곱하기2, // or 곱하기X(2)
곱하기3, // or 곱하기X(3)
더하기4 // or 더하기X(4)
].reduce((prevFunc, nextFunc) => {
return (x) => nextFunc(prevFunc(x)); // 연산
}, init => init); // 최종 결과물 데이터 형태를 reducer 두번째 인자로 넣는다. (입력값을 그대로 반환하는 함수형태)
const result = fomula(10);
console.log(`result: ${result}`); // 64
■ Reduce의 흐름
■ Reduce를 활용한 cleaner 코드
// 2x * 3 + 4
const 곱하기 = (a, b) => a * b;
const 곱하기X = x => a => 곱하기(a, x);
const 곱하기2 = 곱하기X(2);
const 곱하기3 = 곱하기X(3);
const 더하기 = (a, b) => a + b;
const 더하기X = x => a => 더하기(x, a);
const 더하기4 = 더하기X(4);
const compose = (...args) => {
return args.reduce((prevFunc, nextFunc) => {
return (...values) => {
return nextFunc(prevFunc(...values));
}
}, init => init);
}
const fomula = compose(
곱하기2,
곱하기3,
더하기4
);
const result = fomula(10);
console.log(`result: ${result}`);
'클린코드 샘플(JavaScript)' 카테고리의 다른 글
[2] 클린코드 Rule (0) | 2022.11.17 |
---|---|
[1] 클린코드 Rule (0) | 2022.11.17 |
Redux - reducer(switch문) 리팩토링 (0) | 2022.11.15 |
if else, switch (feat. 객체 + 함수) (0) | 2022.11.15 |
if else - 맵핑관계 분석 후 정의(데이터를 객체화, 로직을 분리) (0) | 2022.11.01 |
댓글