본문 바로가기
JavaScript│Node js

함수형 자바스크립트 프로그래밍

by 자유코딩 2018. 10. 23.

1.함수형 프로그래밍이란


함수형 프로그래밍 <-> 객체지향 프로그래밍


모듈 지향            데이터 객체 지향


객체지향 프로그래밍은 프로그램 안의 객체 들의 소통에 집중한다.


그런데 함수형 프로그래밍은 프로그램을 각각의 모듈 들이 조합된 형태로 본다.



Var v1 = 100;

Var v2 = function(){};

Function f1() {return 10;}

Function f2() {return function(){};}



 -> 위의 예시는 var 를 사용했다. 하지만 보통 실제 프로그램을 만들때는 let이나 const 를 많이 쓴다.


const 는 상수이다. 값이 변경 될 수 없다.

var 와 let 은 변수이다. 값이 변경 될 수 있다.


var 와 let 은 차이가 있다.


1
2
var num = 2;
var num  = 3;
cs


이런 코드에서 var 는 문제를 일으키지 않는다.


연속해서 계속 선언 할 수 있다.


그러나 let 은 이렇게 사용하면 구문 에러가 생긴다.


1
2
let num = 2
let num = 3;

cs



let 은 이렇게 쓸 수 없다.


  • 자바스크립트에서 함수는 값처럼 다뤄진다.
이렇게 함수의 리턴으로 함수를 사용 하는 것을 "클로저" 라고 부른다.

아래 코드를 보자.

1
2
3
4
5
6
7
8
9
function getClosure() {
  var text = 'variable 1';
  return function() {
    return text;
  };
}
 
var closure = getClosure();
console.log(closure()); 

csd


이런 코드에서 함수 getClosure의 리턴값으로 얻어지는 함수를 "클로저" 라고 부른다.


로직을 만들때의 특성

  • 절차지향 : 순서대로 처리
  • 객체지향 : 객체들 간의 협업으로 처리
  • 함수지향 : 동일하게 동작하는 함수를 만들고 , 보조함수를 조합


2. 함수형 자바스크립트의 실용성 ( 장점 )


함수형 프로그래밍이 왜 실용적인지 코드를 통해서 살펴보자.


먼저 아래 코드를 보겠다. 아래 코드를 predicate 와 filter를 사용해서 바꿀 것이다.


1
2
3
4
var temp_users = [];
for (var i = 0 , len = users.length ; i< len; i++) {
    if (users[i].age < 30) temp_users.push(users[i]);
}
cs


코드를 바꾸면 이렇게 된다.


1
2
3
4
5
6
7
function filter(list, predicate) {
    var new_list = [];
    for (var i = 0, len = list.length ; i < len; i++) {
        if (predicate(list[i])) new_list.push(list[i]); 
    }
    return new_list;
}
cs


filter 함수는 인자로 list 와 predicate 함수를 받는다.


여기서 list 는 아래와 같이 생겼다.


var list = [
{id : 1, name : "ID", age : 32},
{id : 2, name : "HA", age : 25},
{id : 3, name : "BJ", age : 32},
{id : 4, name : "PJ", age : 28},
{id : 5, name : "JE", age : 27},
{id : 6, name : "JM", age : 32},
{id : 7, name : "HI", age : 24},
];


두 코드는 모두 기존의 리스트의 내용을 비어있는 리스트에 채워넣고 있다.


여기서 predicate 부분의 동작 방식을 살펴보겠다.


위의 코드에서는 if 문 안에 조건을 직접 작성했다.


아래 코드에서는 리스트에 push 할지 여부를 predicate 함수가 결정하게 된다.


함수의 결과가 true 이면 new_list 에 값을 push 한다.


하지만 함수의 결과가 false 이면 new_list 에 값을 push 하지 않는다.


위 코드와 아래 코드 모두 굉장히 쉽게 새로운 리스트에 값을 넣고 있다.


이렇게 새로운 값을 만드는 형태로 값을 다루는 것은 함수형 프로그래밍에서 중요한 부분이다.


당연한 이야기지만 예제에 있는 predicate와 filter는 자바스크립트에서 제공하는 함수는 아니다.


이제 filter를 살펴보겠다. filter는 이런식으로 호출 될 수 있다.


1
2
3
var users_under_30 = filter(users , function (user) {
    return user.age < 30;
});
cs


이렇게 하면 user_under_30에는 30세 미만의 사람들이 저장 될 것이다.


결국 지금 말하고자 하는 핵심은.


함수 지향적으로 프로그램을 작성하면 코드가 간결하다.


함수형 프로그래밍은 함수를 확장 시키면서 동작을 서술한다. - filter 함수 내부에서 predicate 함수를 호출하고 있다.


함수는 동일한 인자가 들어 왔을 때 항상 동일하게 동작한다.



코드 리팩토링의 핵심 - 코드의 중복을 제거하고, 의도를 드러내는 것.


- 직관적인 네이밍 , 가독성 향상 , 코드 모듈화 , 장황하지 않은 코드


map 함수


/*
var ages = [];
for (var i = 0 , len = users_under_30.length ; i < len ; i++) {
ages.push(users_under_30[i].age);
}

console.log(ages);

var names = [];
for (var i = 0, len = users_over_30.length ; i < len ; i++) {
names.push(users_over_30[i].name);
}

console.log(names);
*/

function map(list, iterate) {
var new_list = [];
for (var i = 0 , len = list.length ; i < len ; i++) {
new_list.push(iterate(list[i]));
}
return new_list;
}


list 에 어떤 값들을 push 할지 iterate 함수에게 맡겼다.


사용 할 때는 이렇게 사용한다.


var names = map(users_over_30, function (user) {
return user.name;
});

이렇게 map 을 호출하면 function ( user ) 자리에 iterate가 들어간다.


코드를 상당히 단순하게 만들 수 있다.

 


- 실행 결과로 바로 실행 할 수도 있다.


함수의 리턴 값을 바로 다른 함수의 인자로 사용하면 변수 할당을 줄일 수 있다.


그러면 코드가 이렇게 줄어든다.


var names = map(users_over_30, function (user) {
return user.name;
});

var ages = map(
filter(users , function (user) { return user.name < 30}),
function (user) { return user.age });
console.log(ages.length);

var names = map(
filter(users, function (user) { return user.age >= 30}),
function (user) { return user.name;});

참고 사항.

 

map , find , filter , findIndex , bvalue , bmatch 모두 다 Underscore.js 에 있는 함수들이다.

 

Underscore.js 유명한 함수형 자바스크립트 라이브러리다.

 


4. 함수형 자바스크립트를 위한 기초 


고차함수

1.  함수를 인자로 받아 대신 실행하는 함수

2. 함수를 리턴하는 함수

3. 함수를 인자로 받아서 또 다른 함수를 리턴하는 함수


콜백함수와 익명함수의 차이


모든 익명함수는 콜백함수가 아니다.


일급( 값으로 다룰 수 있다.) 함수


자바스크립트에서 함수는 값으로 다룰 수 있다.


값으로 다룰 수 있다는 것은 아래의 조건을 만족해야 한다.


1.변수에 담을 수 있다.


2. 함수나 메서드의 인자로 넘길 수 있다.


3. 함수나 메서드에서 리턴할 수 있다.



정리


일급 함수 :자바 스크립트에서 함수는 값으로 다룰 수 있다.

클로저 : 외부 함수의 변수에 접근 할 수 있는 내부 함수

차 함수 : 함수를 인자로 받아 대신 실행하는 함수

콜백 함수 : 함수의 매개 변수로 작성된 함수 - 익명 함수는 콜백 함수로 쓰일 수 있다. 그런데 콜백 함수의 형태가 모두 익명함수는 아니다.


일급함수 예제 1 - 변수에 담기


var f1 = function(a){return a;};

console.log(f1(2));


일급함수 예제 2 - 함수를 메서드의 인자로 넘길 수 있다.


1
2
3
4
5
6
7
function map(list, iterate) {
    var new_list = [];
    for (var i = 0 , len = list.length ; i < len ; i++) {
        new_list.push(iterate(list[i]));
    } 
    return new_list;
}
cs


일급함수 예제 3 - 함수나 메서드에서 리턴 할 수 있다.



1
2
3
4
5
6
7
8
9
10
function add_maker(a){
    return function(b) {
        return a+b;
    }
}
 
var add10 = add_maker(10); // 리턴 문에 적인 함수가 반환된다.
// 함수 add10 에는 add_maker를 호출하면서 전달한 10이 a에 들어있다.
 
add10(5); // 5는 b로 전달된다. 그리고 더한 결과가 출력된다.
cs



그러면 이것을 바탕으로 이런 설계를 할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
var f2 = (b) => {
    if(b === 0) {
        return b => {
            return b + 10;
        };
    }
    else if(b === 1) {
        return b => {
            return b - 1;
        };
    }
};
cs


시작 함수 f2에 전달하는 인자에 따라서 로직을 다르게 처리 할 수 있다.


순서도와 같다.


 

 f2의 결과

 

 

 

 f2의 인자가 1인 경우     

 

     f2의 인자가 0인경우

 1을 뺀다.

 

     10을 더한다


클로저 예제


1
2
3
4
5
6
7
8
function closure (a) {
    return function(b) {
        return a+b;
    }
}
 
var c1 = closure(2);
c1(3); // 5 가 출력된다.
cs


closure 를 호출 하면서 보낸 2는 a에 저장된다.


closure 내부에 있는 함수는 외부 함수의 매개변수 a에 접근 할 수 있다.


이런 개념을 클로저라고 부른다.


고차 함수 예제


고차 함수는 함수를 인자로 받아서 대신 실행하는 함수이다.


아래와 같다.


iterate함수를 인자로 받아서 실행하고 있다.


1
2
3
4
5
6
7
function map(list, iterate) {
    var new_list = [];
    for (var i = 0 , len = list.length ; i < len ; i++) {
        new_list.push(iterate(list[i]));
    } 
    return new_list;
}
cs


콜백 함수는 이렇다. 아래 코드처럼 함수의 인자로 작성 된 함수를 말한다.


Todo.deleteOne({_id : todoId},(err, todo) => {
if (err) {
return res.status(500).json({ error : 'database failure' });
}
if (!todo.n) {
return res.status(400).json({ error : 'todo is not found' });
}
return res.status(204).send();
});


이렇게 코드를 작성하면 deleteOne 함수는 todoId를 매개변수로 호출 된다.


그리고 호출 후 (err , todo) 함수의 내용이 실행된다.

댓글