개발의변화
모던자바스크립트 12장 함수 본문
함수: 일련의 과정을 재사용하여 효율적인 프로그래밍 구현
- 함수 정의 function 이름명(매개변수){return 반환값}
- 함수 호출 이름명(인수)
함수는 객체 타입의 값 -> 숫자 값을 숫자 리터럴로 생성하고 객체를 객체 리터럴처럼 함수 리터럴 생성 가능
// 변수에 함수 리터럴을 할당
var f = function add(x, y) {
return x + y;
};
함수 이름은 식별자 네이밍 규칙 준수,
이름이 있는 함수: 기명 함수, 이름이 없는 함수: 무명 함수
함수 선언문 vs 함수 표현식
// 함수 선언문
function add(x, y) {
return x + y;
}
// 함수 참조
// console.dir은 console.log와는 달리 함수 객체의 프로퍼티까지 출력한다.
// 단, Node.js 환경에서는 console.log와 같은 결과가 출력된다.
console.dir(add); // ƒ add(x, y)
// 함수 호출
console.log(add(2, 5)); // 7
함수 리터럴은 함수 이름을 생략할 수 있지만 함수 선언문은 함수 이름을 생략할 수 없다.
자바스크립트 엔진은 생성된 함수를 호출하기 위해 함수 이름과 동일한 이름의 식별자를 암묵적으로 생각하고, 거기에 함수 객체를 할당한다.
함수는 함수 이름으로 호출하는 것이 아니라 함수 객체를 가리키는 식별자로 호출한다.
함수는 일급 객체
일급 개체: 함수를 값처럼 자유롭게 사용할 수 있다는 의미
// 기명 함수 표현식
var add = function foo (x, y) {
return x + y;
};
// 함수 객체를 가리키는 식별자로 호출
console.log(add(2, 5)); // 7
// 함수 이름으로 호출하면 ReferenceError가 발생한다.
// 함수 이름은 함수 몸체 내부에서만 유효한 식별자다.
console.log(foo(2, 5)); // ReferenceError: foo is not defined
// 함수 참조
console.dir(add); // ƒ add(x, y)
console.dir(sub); // undefined
// 함수 호출
console.log(add(2, 5)); // 7
console.log(sub(2, 5)); // TypeError: sub is not a function
// 함수 선언문
function add(x, y) {
return x + y;
}
// 함수 표현식
var sub = function (x, y) {
return x - y;
};
함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수의 생성 시점이 다르다.
var: 선언된 변수 undefined 초기화
함수 선언문: 암묵적으로 생성된 식별자는 함수 객체로 초기화된다.
함수 표현식: 함수를 정의하면 함수 호이스팅을 발생하는 것이 아니라 변수 호이스팅 발생
하지만 const,let의 함수 표현식은 initialization error가 뜬다.
const add = (a,b) => a+b
화살표 함수: 기존 함수와 this 바인딩 방식이 다르고, prototype 프로퍼티가 없으며 arguments 객체를 생성X
12.5 함수 호출
function add(x, y) {
console.log(x, y); // 2 5
return x + y;
}
add(2, 5);
// add 함수의 매개변수 x, y는 함수 몸체 내부에서만 참조할 수 있다.
console.log(x, y); // ReferenceError: x is not defined
function add(x, y) {
return x + y;
}
console.log(add(2)); // NaN
함수는 매개변수의 개수와 인수가 일치하는지 체크하지 않는다.
매개변수 x에는 인수 2가 전달되지만, 매개변수 y에는 전달할 인수가 없다.
즉 NaN이 반환된다.
또한 인수가 매개변수보다 많으면 없앤다.
function add(x, y) {
return x + y;
}
console.log(add(2)); // NaN
console.log(add('a', 'b')); // 'ab'
-> 매개변수의 타입을 확인할 수 없고, 인수의 개수의 제한이 없기에 arguments를 통해 인수 개수 확인할 수 있고 또한
인수가 전달되지 않은 경우 단축 평가를ㄹ 이용해 기본값 할당
function add(a, b, c) {
a = a || 0;
b = b || 0;
c = c || 0;
return a + b + c;
}
console.log(add(1, 2, 3)); // 6
console.log(add(1, 2)); // 3
console.log(add(1)); // 1
console.log(add()); // 0
매개변수는 최대한 작게 만들어야 하고 함수는 한 가지 일만 해야 한다.
return 뒤의 반환값이 없으면 undefined로 반환된다.
// 매개변수 primitive는 원시 값을 전달받고, 매개변수 obj는 객체를 전달받는다.
function changeVal(primitive, obj) {
primitive += 100;
obj.name = 'Kim';
}
// 외부 상태
var num = 100;
var person = { name: 'Lee' };
console.log(num); // 100
console.log(person); // {name: "Lee"}
// 원시 값은 값 자체가 복사되어 전달되고 객체는 참조 값이 복사되어 전달된다.
changeVal(num, person);
// 원시 값은 원본이 훼손되지 않는다.
console.log(num); // 100
// 객체는 원본이 훼손된다.
console.log(person); // {name: "Kim"}
실행함수
// 익명 즉시 실행 함수
(function () {
var a = 3;
var b = 5;
return a * b;
}());
재귀함수
function countdown(n) {
if (n < 0) return;
console.log(n);
countdown(n - 1); // 재귀 호출
}
countdown(10);
// 팩토리얼(계승)은 1부터 자신까지의 모든 양의 정수의 곱이다.
// n! = 1 * 2 * ... * (n-1) * n
function factorial(n) {
// 탈출 조건: n이 1 이하일 때 재귀 호출을 멈춘다.
if (n <= 1) return 1;
// 재귀 호출
return n * factorial(n - 1);
}
console.log(factorial(0)); // 0! = 1
console.log(factorial(1)); // 1! = 1
console.log(factorial(2)); // 2! = 2 * 1 = 2
console.log(factorial(3)); // 3! = 3 * 2 * 1 = 6
console.log(factorial(4)); // 4! = 4 * 3 * 2 * 1 = 24
console.log(factorial(5)); // 5! = 5 * 4 * 3 * 2 * 1 = 120
탈출조건이 없으면 스택 오버플로 에러가 발생한다.
콜백함수
// 콜백 함수를 사용하는 고차 함수 map
var res = [1, 2, 3].map(function (item) {
return item * 2;
});
console.log(res); // [2, 4, 6]
// 콜백 함수를 사용하는 고차 함수 filter
res = [1, 2, 3].filter(function (item) {
return item % 2;
});
console.log(res); // [1, 3]
// 콜백 함수를 사용하는 고차 함수 reduce
res = [1, 2, 3].reduce(function (acc, cur) {
return acc + cur;
}, 0);
console.log(res); // 6
순수함수: 어떤 외부 상태에 의존하지도 않고 변경하지도 않는, 즉 부수 효과가 없는 순수함수
비순수함수: 외부 상태에 의존하거나 외부 상태를 변경하는 함수
var count = 0; // 현재 카운트를 나타내는 상태
// 순수 함수 increase는 동일한 인수가 전달되면 언제나 동일한 값을 반환한다.
function increase(n) {
return ++n;
}
// 순수 함수가 반환한 결과값을 변수에 재할당해서 상태를 변경
count = increase(count);
console.log(count); // 1
count = increase(count);
console.log(count); // 2
var count = 0; // 현재 카운트를 나타내는 상태: increase 함수에 의해 변화한다.
// 비순수 함수
function increase() {
return ++count; // 외부 상태에 의존하며 외부 상태를 변경한다.
}
// 비순수 함수는 외부 상태(count)를 변경하므로 상태 변화를 추적하기 어려워진다.
increase();
console.log(count); // 1
increase();
console.log(count); // 2