chapter13. 함수와 추상적 사고

13.1 서브루틴으로서의 함수

서브루틴 : 프로시저, 루틴, 서브프로그램, 매크로 등 다양하게 불린다.
복잡한 코드를 간단하게 만드는 기초적인 수단이다.
반복되는 작업의 일부를 떼어내서 이름을 붙이고 언제든지 이름을 부르면 실행된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//오늘이 윤년인지 아닌지 판단하는 알고리즘
const year = new Date().getFullYear();
//console.log(year); //2019
if(year % 4 !== 0) console.log(`${year} is not a leap year`);
if(year % 100 !== 0) console.log(`${year} is not a leap year`);
if(year % 400 !== 0) console.log(`${year} is not a leap year`);
else console.log(`${year} is a leap year`);

//오늘이 윤년인지 아닌지 판단하는 함수
function printLeapYearStatus(){
const year = new Date().getFullYear();
//console.log(year); //2019
if(year % 4 !== 0) console.log(`${year} is not a leap year`);
if(year % 100 !== 0) console.log(`${year} is not a leap year`);
if(year % 400 !== 0) console.log(`${year} is not a leap year`);
else console.log(`${year} is a leap year`);
}

13.2 값을 반환하는 서브루틴으로서의 함수

서브루틴 함수
1
2
3
4
5
6
7
8
9
10
11
12
function isCurrentYearLeapYear(){
const year = new Date().getFullYear();
if(year % 4 !== 0) return false;
if(year % 100 !== 0) return false;
if(year % 400 !== 0) return false;
else return true;
}

//값 반환
const dayInMonth = [31,isCurrentYearLeapYear() ? 29:28, 30,31,30,31,31,30,31,30,31];
if(isCurrentYearLeapYear()) console.log('it is a leap year');
console.log(dayInMonth); // [ 31, 28, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]

13.3 함수로서의 함수

순수한 함수 :

  • 입력이 같으면 결과도 반드시 같다.
    앞의 isCurrentYearLeapYear()는 언제 호출하느냐에 따라 값이 달라지므로 순수함수가 아니다.
  • 순수함수에는 부수효과가 없다. 즉, 함수를 호출한다고 해서 프로그램의 상태가 바뀌면 안됌
순수함수 아닐때
1
2
3
4
5
6
7
// #순수함수 - 순수하지 않은 함수(레인보우)
const colors = ['red','orrange','yellow','green','blue','indigo','violet'];
let colorIndex = -1;
function getNextRaindowColor(){
if(++colorIndex >= colors.length) colorIndex = 0;
return colors[colorIndex];
}
이 함수는 순수함수의 두가지 정의를 모두 어긴다.
1. 입력이 같아도 결과가 항상 다르고,
2. 변수 colorIndex를 바꾸는 부수효과가 발생한다.
(colorIndex는 getNextRaindowColor 함수에 속하지 않는데도 함수를 호출하면 바뀜 
= > 부수효과 발생)
순수함수로 레인보우 수정
1
2
3
4
5
6
7
8
9
10
11
12
// #순수함수 - 순수한함수(레인보우)
//이터레이터
function getRainbowIterator(){
const colors = ['red','orrange','yellow','green','blue','indigo','violet'];
let colorIndex = -1;
return{
next(){
if(++colorIndex >= colors.length) colorIndex = 0;
return {value : colors[colorIndex], done : false}
}
}
}
이 함수는 순수함수다.
1. 입력이 같으면 결과가 항상같다.
2. 부수효과가 발생하지 않는다.

next() 메서드는 매번 다른 값을 반환할테니 순수함수가 아니지 않은가에 대한 해설
next()는 함수가 아니라 메서드이다.
메서드는 자신이 속한 객체라는 컨텍스트 안에서만 동작하므로,
메서드의 동작은 그 객체의 의해 좌우된다.
즉 다른부분에서 getRainbowIterator()를 호출해도 독립적인 이터레이터가 생성된다.

13.4 그래서?

함수의 세가지 측면 : 서브루틴, 값을 반환하는 서브루틴, 순수함수에 대해

  • 서브루틴 : 반복을 없애준다.
  • 순수한 함수 : 코드를 테스트하기 쉽고 이해하고 재사용하기 쉽다.
    순수함수는 부수효과를 발생시키지 않으므로 버그를 줄여준다.

13.4.1 함수도 객체다

자바스크립트 함수는 Function 객체의 인스턴스이다.
typeof v => v가 함수일 때 “function”을 반환
typeof array => array가 배열이면 “object”를 반환
v instanceof object => true를 반환한다.
그러므로 변수가 함수인지 확인하고 싶을 땐 typeof를 써보기

13.5 IIFE와 비동기적 코드

IIFE(즉시 호출하는 함수 표현식), 클로저를 만들 수 있음
IIFE는 비동기적 코드가 정확히 동작할 수 있도록 새 변수를 스코프에 만든다.

1
2
3
4
5
6
7
//5초에서 시작하고 카운트다운이 끝나면 "GO"를 표시하는 타이머
var i;
for(i = 5; i>=0; i--){
setTimeout(function(){
console.log(i===0? "GO": i);
}, (5-i)*1000);
} //-1이 6번 출력
setTimeout에 전달된 함수가 루프 안에서 실행되지 않고 루프가 종료된 뒤에 실행되었다.
루프는 5에서 시작해 -1로 끝난다.
-1이 되기 전에 콜백함수가 실행되지 않는다.
콜백함수가 호출되는 시점에서 i의 값은 -1이다.
그러므로 -1이 6번 출력된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//5초에서 시작하고 카운트다운이 끝나면 "GO"를 표시하는 타이머
//위에 함수로 감쌈
function loopBody(i){
setTimeout(function(){
console.log(i===0? "GO": i);
}, (5-i)*1000);
}
var i;
for(i = 5; i>=0; i--){
loopBody(i);
}

/*
5
4
3
2
1
GO
*/

//IIFE로 고쳐쓰기
/*한번쓰고 버릴 함수는 이름을 붙이는게 성가심*/
var i;
for(i = 5; i>=0; i--){
(function(i){
setTimeout(function(){
console.log(i===0? "GO" :i);
}, (5-i)*1000);
})(i);
}

13.6 변수로서의 함수

  • 함수를 가리키는 변수를 만들어 별명을 정할 수 있다.
  • 배열에 함수를 넣을 수 있다. 물론 다른 타입의 데이터와 섞일 수 있다.
  • 함수를 객체의 프로퍼티로 사용할 수 있다.
  • 함수를 함수에 전달할 수 있다.
  • 함수가 함수를 반환할 수 있다.
  • 함수를 매개변수로 받는 함수를 반환하는 것도 가능하다.
함수에 별명정하기
1
2
3
4
5
6
7
8
9
//함수를 가리키는 변수를 만들어 별명을 정할 수 있다.
function addThreeSquareAddFiveTakeSqureRoot(x){
return Math.sqrt(Math.pow(x+3,2)+5);
}

//별명쓰기
const f = addThreeSquareAddFiveTakeSqureRoot;
const answer = (f(5) + f(2)) / f(7);
//7.3484692283495345
별명을 지을 때 addThreeSquareAddFiveTakeSqureRoot에
괄호를 붙이지 않음
괄호를 붙이면 함수를 호출하고, 호출 결과가 저장되게됨

13.6.1 배열 안의 함수

배열 안의 함수를 쓰는것은 유용하다.
자주 하는 일을 한 셋으로 묶는 파이프라인이 좋은 예

13.6.2 함수에 함수 전달

비동기적 프로그래밍
콜백함수 : 자신을 감싼 함수가 실행을 마쳤을 때 호출
함수에 함수를 전달하는 방식

함수전달
1
2
3
4
5
6
7
function sum(arr,f){
//함수가 전달되지 않았으면 매개변수를 그대로 반환하는 null 함수를 씁니다.
if(typeof f !='function') f = x=>x;
return arr.reduce((a,x) => a += f(x),0);
}
sum([1,2,3]);//6
sum([1,2,3], x => x*x);//14

13.6.3 함수를 반환하는 함수

함수반환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function sum(arr,f){
//함수가 전달되지 않았으면 매개변수를 그대로 반환하는 null 함수를 씁니다.
if(typeof f !='function') f = x=>x;
return arr.reduce((a,x) => a += f(x),0);
}
sum([1,2,3]);//6
sum([1,2,3], x => x*x);//14

function newSummer(f){
return arr => sum(arr,f);
}
const sumOfSquares = newSummer(x => x*x);
const sumOfCubs = newSummer(x => Math.pow(x,3));
sumOfSquares([1,2,3]); //14
sumOfCubs([1,2,3]); //36

13.7 재귀

재귀함수 : 자기 자신을 호출하는 함수
같은 일을 반복하면서 그 대상이 점차 줄어드는 상황에서 재귀를 유용하게 쓸 수 있다.

  • 재귀 함수에는 종료 조건이 있어야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//건초더비에서 바늘을 찾아야하는 예제
function findNeedle(haystack){
if(haystack.length===0) return "no haystack here";
if(haystack.shift() === 'needle') return "found it"
return findNeedle(haystack)//건초더미의 건초가 하나 줄었다.
}
findNeedle(['hay','hay','hay','hay','needle','hay','hay',]);
/*
"no haystack here"
"no haystack here"
"no haystack here"
"no haystack here"
"found it"
"no haystack here"
"no haystack here"
*/


//숫자의 계승을 찾는 예제
function fact(n){
if(n === 1) return 1;
return n * fact(n-1);
}

Comentarios

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×