즉, 명령형 프로그래밍은 무엇을 "어떻게" 할 것인가에 가깝고, 선언형 프로그래밍은 "무엇을" 할 것인가와 가깝다.
실세계의 예제를 살펴보면 이해가 쉽다.
나는 회사에서 열심히 일하고 퇴근한 뒤 A에서 가족과 외식을 하기로 했다. A에 도착한 뒤 안내 데스크에 다가가 말한다.
- 명령형 방식(How): "12번 테이블 자리가 비었습니다. 우리 가족은 저자리로 걸어가 앉을 것입니다."
- 선언형 방식(What): "4명 앉을 자리를 부탁해요."
위 예시를 보면, 명령형 방식은 내가 실제로 자리에 "어떻게" 앉을지에 관심이 있다. 따라서 "어떻게" 자리에 앉을지에 대한 단계를 하나 하나 "나열"해야 한다.
반대로 선언형 방식은 내가 "무엇을" 원하는지에 더 집중되어 있다.
// 명령형
function double (arr) {
let results = []
for (let i = 0; i < arr.length; i++){
results.push(arr[i] * 2)
}
return results
}
function add (arr) {
let result = 0
for (let i = 0; i < arr.length; i++){
result += arr[i]
}
return result
}
$("#btn").click(function() {
$(this).toggleClass("highlight")
$(this).text() === 'Add Highlight'
? $(this).text('Remove Highlight')
: $(this).text('Add Highlight')
})
// 선언형
function double (arr) {
return arr.map((item) => item * 2)
}
function add (arr) {
return arr.reduce((prev, current) => prev + current, 0)
}
<Btn
onToggleHighlight={this.handleToggleHighlight}
highlight={this.state.highlight}>
{this.state.buttonText}
</Btn>
부수 효과(side effect)를 없애고 순수 함수의 조합성을 강조하는 프로그래밍 패러다임이다. 함수형 프로그래밍은 명령형(imperative)이 아닌 선언형(declarative)이며, 애플리케이션의 상태는 순수 함수를 통해 전달된다.
사이드 이펙트가 없다.
순수함수의 조합으로 이루어지기 때문에 결과값은 변하지 않는다.
간결하다.
함수형 프로그래밍의 중요 개념인 Curry, Partial Application, Monad와 같은 기법이 간결하고 우아한 함수의 구성(Composition)을 가능하게 해준다.
단, 현재 React에서는 hooks의 state를 통해 함수형 프로그래밍 방식으로도 상태를 가질수 있다.
1급 객체란 다음과 같은 조건을 만족하는 객체를 말한다.
자바스크립트에서 함수(Function)은 1급 객체이다.
고차 함수는 아래 조건을 만족하는 함수를 말한다.
고차 함수는 1급 함수의 부분 집합이다. 리액트 고차 컴포넌트(HOC)는 위의 조건을 만족하는 컴포넌트를 말한다.
함수형 프로그래밍에서는 함수 외부의 데이터를 변경하지 않도록 한다. 만약 데이터 변경이 필수적일 경우, 원본 데이터를 변경하지 않고 그 데이터의 복사본을 만들어 변경 작업을 진행한다.
const red={name:'red'};
// 원본 데이터를 변경하는 방법💩
funcion changeColor(color,name){
color.name=name;
return color;
}
console.log(changeColor(red,'yellow')); //{name:'yellow'}
console.log(red); //{name:'yellow'}
// 불변성을 지키는 방법👍
function changeColor(color,name){
return Object.assign({},color,{name});
}
console.log(changeColor(red,'yellow')); //{name: 'yellow'}
console.log(red.name); //{name: 'red'}
아래의 메소드들은 함수형 프로그래밍 개념에 따라 기존 변수에 대한 사이드 이펙트가 없도록 구현되어 있다. 해당 메소드들을 사용하여 데이터의 복사본을 변경하는 방식으로 데이터의 불변성을 지켜주자.
const arr = ['foo', 'hello', 'diamond', 'A'];
const arr2 = arr.map((v) => v.length);
console.log(arr2) // [3, 5, 6, 1]
const arr = [4, 15, 377, 395, 400, 1024, 3000];
const arr2 = arr.filter((v) => (v % 5 === 0));
console.log(arr2) // [15, 395, 400, 3000]
let arr = [9, 2, 8, 5, 7];
let sum = arr.reduce((pre, val) => pre + val);
console.log(sum) // 31
아래의 조건을 만족하는 함수를 순수함수라고 한다.
const arr = [1, 2, 3, 4, 5];
const condition = function(x) { return x % 2 === 0; }
const ex = function(array) {
return array.filter(condition); //인자로 받지 않은 변수를 사용하고 있음
};
ex(arr); // [2, 4]
const arr = [1, 2, 3, 4, 5];
const condition = function(x) { return x % 2 === 0; }
const ex=function(array,cond){
return array.filter(cond);
}
ex(arr,condition);
합성 함수는 둘 이상의 함수를 조합한 함수를 말한다.
함수형 프로그래밍은 여러 작은 순수 함수들로 이루어져있기 때문에 이 함수들을 연쇄적으로 또는 병렬로 호출하여 더 큰 함수를 만드는 과정으로 전체 프로그램을 구축해야 한다.
const sum=(a,b)=>a+b;
const square=x=>x*x;
const addTen=x=>x+10;
const computeNumbers=addTen(square(sum(3,5)))//74
//compose는 함수를 연쇄적으로 호출하면서 반환값을 전달한다.
const compose=(...fns)=>fns.reduce((prev,next)=>(...args)=>next(prev(...args)));
//compose 사용
const compute=compose(sum, square, addTen);
compute(3,5) //74
반복문을 사용하지 말라고 했는데, 그러면 어떻게 처리해야할지 예제를 통해 살펴보자.
보통 1부터 10까지 더할 때 아래와 같이 많이 한다.
let sum=0;
for (let i=1;i<=10;i++){
sum+=i;
}
함수형 프로그래밍에서는 다음과 같이 재귀함수를 사용하면 된다.
function add(sum,count){
if(count>0){
sum+=count;
return add(sum,count-1);
}else{
return sum;
}
}
add(0,10); //55
위와 같이 add 안에서 add를 또 호출하는 게 복잡해 보일 수 있으나, 실행 결과를 생각해봤을 때 이렇게 하면 코드의 재사용성이 매우 높아진다.
다른 방법으로 reduce 메소드를 쓸 수도 있다.
const arr = [1,2,3,4,5,6,7,8,9,10];
arr.reduce((prev, cur)=> prev + cur); // 55