미리 정의된 함수를 재사용하는 것이 효율적
객체 타입
함수를 호출하기 이전에 인수를 전달받을 매개변수와 실행할 문들, 그리고 반환할 값을 지정하는 것
1) 함수 선언문
function add(x, y) {
return x + y;
}
2) 함수 표현식
var add = function(x, y) {
return x + y;
}
3) Function 생성자 함수
var add = new Function('x', 'y', 'return x+y')
4) 화살표 함수(ES6)
var add = (x, y) => x + y;
변수 선언과 함수 정의
변수는 선언한다고 했지만, 함수는 정의한다고 함
함수 선언문이 평가되면 식별자가 암묵적으로 생성되고 함수 객체가 할당됨
// 함수 선언문은 함수 이름을 생략할 수 없음
function (x, y) {
return x + y;
}
// 하지만 함수 선언문이 변수에 할당되는 것처럼 보임
var add = function add(x, y) {
return x + y;
};
console.log(add(2, 5)); // 7
=> 이렇게 동작하는 이유는?
자바스크립트 엔진이 코드의 문맥에 따라 동일한 함수 리터럴을 문인 함수 선언문으로 해석
하는 경우, 표현식인 문인 함수 리터럴 표현식으로 해석
하는 경우가 있기 때문임
기명 함수 리터럴
함수에 이름을 지정하여 정의하는 방식var funcLiteral = function namedFunction() { console.log("기명 함수 리터럴"); };
함수 선언문
function funcDeclaration() {
console.log("함수 선언문");
}
함수 리터럴 표현식
var funcExpression = function() {
console.log("함수 리터럴 표현식");
};
P.161-162 다시보기
// 기명 함수 표현식
var add = function foo(x, y) {
return x + y;
};
console.log(add(2, 5)); // 7
console.log(foo(2, 5)); // ReferenceError
함수 선언문 : 표현식이 아닌 문
함수 표현식 : 표현식인 문
- 함수 선언문으로 정의한 함수 : 함수 선언문 이전에 호출 가능
- 함수 표현식으로 정의한 함수 : 함수 표현식 이전에 호출 불가
=> 함수 선언문도 런타임 이전에 실행됨
function add(x, y) {
return x + y;
}
console.log(add(2)); // NaN
이상적인 매개변수 개수는 0개
1) 반환문은 함수의 실행을 중단하고 함수 몸체를 빠져나감
따라서 반환문 이후에 다른 문이 존재하면 그 문은 실행되지 않고 무시됨
2) 반환문은 return 키워드 뒤에 오는 표현식을 평가해 반환함
return 키워드 뒤에 반환값으로 사용할 표현식을 명시적으로 지정하지 않으면 undefined가 반환됨
1) 예시
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"}
원시 타입 인수
- 값 자체가 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 그 값을 변경해도 원본 훼손 안됨
객체 타입 인수- 참조 값이 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 참조 값을 통해 객체를 변경할 경우 원본이 훼손됨
=> 즉, 함수 외부에서 함수 몸체 내부로 전달한 참조 값에 의해 원본 객체가 변경되는부수 효과
가 발생함
=> 함수가 외부 상태를 변경하면 상태 변화를 추적하기 어려워짐❓ 이러한 문제의 해결방법
1) 객체를 불변 객체로 만들어서 사용하기
깊은 복사
를 통해 새로운 객체를 생성하고, 재할당을 통해 교체함
2) 순수 함수- 외부 상태를 변경하지 않고 외부 상태에 의존하지도 않는 함수
- 함수형 프로그래밍 : 순수 함수를 통해 부수효과를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이려는 프로그래밍 패러다임
그룹 연산자란❓
그룹 연산자 () 는 표현식 내에서 평가의 우선순위를 제어함
재귀 호출
함수가 자기 자신을 호출하는 것
재귀 함수
1) 반복문을 써서 출력하는 함수
function countdown(n) {
for (var i = n; i >= 0; i--) console.info(i);
}
countdown(10);
2) 반복문없이 재귀 함수로 구현
function countdown(n) {
if (n < 0) return;
console.log(n)
countdown(n - 1) // 재귀 호출
}
countdown(10)
결론
재귀 함수
는 반복되는 처리를 반복문 없이 구현할 수 있다는 장점이 있지만,
무한 반복에 빠질 위험이 있고, 이로 인해 스택 오버플로 에러를 발생시킬 수 있으므로 주의해서 사용해야함
1) 예시 - 어떤 일을 반복 수행하는 repeat 함수
를 정의해보자
function repeat(n) {
for (var i = 0; i < n; i++) console.info(i);
}
repeat(5);
=> repeat 함수
는 매개변수를 통해 전달받은 숫자만큼 반복하며 console.log(i)
를 호출함
이 때, repeat 함수
는 console.log(i)에 강하게 의존하고 있어 다른 일을 할 수 없음
따라서 repeat 함수
의 반복문 내부에서 다른 일을 하고 싶다면 함수를 새롭게 정의해야함
2) 콜백함수
3) 고차함수
(1) map
map
함수는 배열의 각 요소에 대해 주어진 함수를 호출하고 그 결과를 새로운 배열로 반환함const numbers = [1, 2, 3, 4, 5];
const squaredNumbers = numbers.map(function(num) {
return num * num;
})
console.log(squaredNumbers) // [1, 4, 9, 16, 25]
map
함수는 배열 numbers
의 각 요소를 제곱하여 새로운 배열 squaredNumbers
를 생성함map
함수는 인자로 받은 함수(콜백 함수)를 자신의 일부분으로 합성하여 사용함(2) filter
filter
함수는 주어진 함수의 반환값이 true
인 요소들로 이루어진 새로운 배열을 반환함const numbers = [1, 2, 3, 4, 5];
// 고차함수인 filter를 사용하여 numbers 배열에서 짝수만 필터링한 새로운 배열을 생성합니다.
const evenNumbers = numbers.filter(function(num) {
return num % 2 === 0;
});
console.log(evenNumbers); // [2, 4]
filter
함수는 배열 numbers
에서 짝수인 요소만 필터링하여 새로운 배열 evenNumbers
를 생성함filter
함수는 인자로 받은 함수(콜백 함수)를 자신의 일부분으로 합성하여 사용함
map
과filter
함수는 각각 주어진 함수(콜백 함수)를 자신의 일부분으로 합성하여 사용함
이러한 방식으로 고차함수를 사용하면 코드를 간결하게 작성하고, 반복적인 작업을 추상화하여 효율적인 코드를 작성할 수 있다.
즉, 순수 함수는 어떤 외부 상태에도 의존하지 않으며 외부 상태를 변경하지도 않는 함수
var count = 0;
function increase() {
return ++count;
}
increase();
console.log(count); // 1
increase();
console.log(count); // 2
비순수 함수는 코드의 복잡성을 증가시키기때문에 최대한 줄이는 것이 부수 효과를 최대한 억제하는 것과 같음