12.1 함수란?
- 입력을 받아 출력을 내보내는 일련의 과정
// 함수 정의
function add(x, y) { // x, y :매개변수
return x + y; // 반환값
};
// 함수 호출
add(2, 5); // 2, 5: 인수
12.2 함수를 사용하는 이유
- 코드 재사용 가능 !
- 내부 코드를 보지 않고도 식별자를 보고 함수의 역할을 파악할 수 있게 해야한다.
12.3 함수 리터럴
- 함수는 객체다.
- 그러나 일반 객체는 호출할 수 없지만 함수는 호출이 가능하다.
- 함수 이름은 생략할 수 있다.
- 기명 함수와 익명 함수가 존재
- 익명 함수 : 이름이 없는 것이 아닌 함수 이름 대신 변수에 함수를 대입한 방식이다.
let anonymousFunc = function() {
console.log('this function is at variable!!');
}
❓ 왜 익명 함수를 쓰나요? 익명함수?
12.4 함수 정의
- 함수 정의 방법
- 함수 선언문
- 함수 표현식
- Function 생성자 함수
- 화살표 함수(ES6)
12.4.1 함수 선언문
- 표현식이 아닌 문이다.
- 변수에 할당할 수 없다.
- 자바스크립트 엔진은 함수 선언문의 함수이름을 암묵적인 식별자로 사용한다.
function add(x, y) {
return x + y;
}
// 함수의 프로퍼티도 출력
console.dir(add); // ƒ add(x, y)
// 함수 호출
console.log(add(3, 5)); // 8
// 변수에 할당된 것처럼 보일 뿐, 함수 리터럴 형태로 해석된다.
const add = function test(x, y) {
return x + y;
};
console.log(add(2, 5)); // 7
console.log(test(2, 5)); // Reference Error!
- 자바스크립트 엔진은 문맥에 따라 해석이 달라질 수 있다.
- 연산자 내에 있는 함수는 피연산자로 인식되어 함수리터럴로 해석하게 된다.
- 함수의 이름은 함수 몸체를 참조할 수 있는 식별자이기 때문에 해당 함수는 식별자가 없어 참조에러가 발생한다.
(function add(x, y) {
return x + y;
}); // 참조에러
12.4.2 함수 표현식
- 자바스크립트의 함수는 일급 객체이다.
- 함수 리터럴로 생선한 함수 객체를 변수에 할당하면 함수 표현식이다.
- 표현식인 문이다.
- 변수에 할당할 수 있다.
12.4.3 함수 생성 시점과 함수 호이스팅
- 함수 선언문은 함수 호이스팅이 발생한다.
- 함수 표현식은 변수에 할당되어 있으므로 변수 호이스팅이 발생한다.
- 런타임 이전 undefined로 초기화되었다가 할당문이 실행되는 시점에 함수 객체가 된다.
- 함수 표현식을 권장한다.
12.4.4 Function 생성자 함수
- new 연산자와 함께 호출하거나, 쓰지 않아도 생성된다.
- 이 방식은 권장하지 않는 방식이며, Function 생성자 함수로 생성한 함수는 클로저를 생성하지 않는다.
- 함수 선언문이나 함수 표현식과 다르게 동작한다.
- 클로저에 대한 내용을 알기 위해서는 스코프에 대한 내용도 알고 있어야 한다.
const add = new Function('x', 'y', 'return x + y');
console.log(add(2, 5)); // 7
12.4.5 화살표 함수
- 화살표 함수는 익명함수이다.
- 기존 함수에서 표현 방식과 내부 동작이 간략화 되었다.
const add = (x, y) => x + y;
console.log(add(2, 5)); // 7
12.5 함수 호출
- 함수를 호출하면 현재 실행 흐름을 중단하고 호출된 함수가 먼저 실행된다.
12.5.1 매개변수와 인수
- 함수 외부 값을 함수 내부에서 사용하기 위해서는 매개변수를 통해 인수를 전달한다.
- 인수는 개수와 타입에 제한이 없다.
- 매개변수는 함수를 정의할 때 선언되며 일반 변수와 같이 undefined로 초기화 되었다가 인수가 할당된다.
- 매개변수는 함수 내부에서만 참조가능하며 외부에서는 참조할 수 없다.
- 스코프는 함수 내부다.
function add(x, y) {
console.log(x, y);
return x + y;
}
add(2, 5); // 7
console.log(x, y); // Reference Error
- 함수는 매개변수와 인수의 개수가 일치하지 않는지 체크하지 않아서 인수가 부족해도 에러가 발생되지 않고, 인수가 없으면 초기화 값인 undefined로 계산된다.
- 초과된 인수는 무시된다.
- 버려지는 것은 아니고 arguments 객체의 프로퍼티로 보관된다.
- 매개변수의 개수를 확정할 수 없는 가변 인자 함수를 구현할 때 이용된다.
function add(x, y){
console.log(arguments); // Arguments(3) [2, 5, 10, callee: ƒ, Symbol(Symbol.iterator): ƒ]
return x + y;
}
add(2, 5, 10); // 7
12.5.2 인수 확인
- 자바스크립트는 동적 타입 언어이기 때문에 함수는 매개변수의 타입을 사전에 지정할 수 없다.
- 함수를 만든 개발자의 의도와 다른 타입의 값이 들어와도 에러가 나지 않는다.
- 이런 현상을 방지하기 위해 함수 내부에서 올바른 타입이 들어오지 않으면 에러를 뱉는 조건문을 추가하거나, 인수가 들어오지 않았을 상황에 인수의 기본값을 할당하는 방법도 있다.
- 또는 정적 타입을 선언 가능한 Typescript를 사용한다.
12.5.3 매개변수의 최대 개수
- ECMAScript에서 제한하는 매개변수의 최대 개수는 제한하고 있지 않으나 물리적으로는 있다.
- 매개변수가 많다는 것은 해당 함수가 여러가지 일을 하고 있다는 뜻이며 바람직하지 않다.
- 함수는 하나의 일만 해야한다.
- 매개변수는 최대 3개 이상을 넘지 않는 것을 권장하며 개수가 많아진다면 객체를 활용해 인수를 전달하자.
- 얕은 참조로 객체 전달시 함수 내부에서 객체를 변경시키면 외부에도 영향을 가니 주의하자.
12.5.4 반환문
- return을 사용해 실행 결과를 함수 외부로 전달할 수 있다.
- 반환문의 역할
- 반환문 이후 다른 문은 실행하지 않는다. 반환문이 실행되는 즉시 함수를 빠져나간다.
- return 뒤에 오는 표현식을 반환한다. 반환값이 표현식을 지정하지 않으면 undefined가 반환된다.
- 반환문 생략시 undefined 반환
function foo() {}
console.log(foo()); // undefined
- return 문과 반환값 사이에 줄바꿈이 있으면 의도하지 않는 결과가 나온다.
- return 뒤에는 암묵적으로 세미콜론이 추가되어 문장이 끝난다.
- 반환문은 함수 내부에서만 사용할수 있다.
- 함수 외부 사용시 SyntaxError가 발생한다.
Node.js는 파일별로 스코프를 갖기 때문에 파일의 바깥 영역에 반환문을 사용해도 에러가 나지 않는다.
12.6 참조에 의한 전달과 외부 상태의 변경
- 원시값은 값에 의한 전달, 객체는 참조의 의한 전달 방식으로 동작하며 이는 함수 내부에서도 동일하게 동작한다.
function changeVal(primitive, obj) {
primitive += 100;
obj.name = 'Kim';
}
const num = 100;
const person = { name: 'Lee' };
changeVal(num, person);
console.log(num); // 100
console.log(person); // {name: 'Kim'}
// 객체는 참조값을 사용하기 때문에 훼손된다.
- 외부에서 사용되는 객체가 함수 내부에서 값이 변경되면 오류를 추적하기 어려워진다.
- 객체 변경을 추적하기 위해 옵저버 패턴 등을 이용하여 추가 대응이 필요하다.
- 객체를 불변 객체로 만들어 사용한다. → 비용은 들지만 깊은 복사를 활용해 버그 가능성을 줄인다.
12.7 다양한 함수의 형태
12.7.1 즉시 실행 함수 : IIFE (Immediately Invoked Function Expression)
- 정의와 동시에 즉시 호출되는 함수
- 결과 값이 바로 나온다.
- (…)로 감싼다.
- 단 한번 호출되면 두 번 이상 호출은 없다.
- 익명함수로 사용한다.
(function () {
let a = 3;
let b = 4;
return a * b;
}()); // 12
- 기명함수로 사용할시
(function f() {
let a = 3;
let b = 4;
return a * b;
}()); // SyntaxError
// 함수 선언 뒤에 암묵적으로 세미콜론이 추가된다.
- 기명 함수를 그룹 연산자로 감싸면 함수 리터럴로 평가되어 함수 객체가 된다.
- 값 반환도 가능하고, 인수 전달도 가능하다.
const res = (function (a,b) {
return a * b;
}(3, 5));
console.log(res); // 15
12.7.2 재귀 함수
- 함수가 자기 자신을 호출하는 것을 재귀 호출이라 한다.
- 재귀 호출을 하는 함수를 말한다.
- 반복문을 사용하지 않고, 반복 처리를 위해 사용된다.
function countdown(n) {
if (n < 0) return;
console.log(n);
countdown(n - 1);
}
countdown(10); // 10 9 8 ... 1
- 함수 표현식을 사용한다면 함수 식별자를 이용하여 호출해야한다.
- 재귀 함수는 자신을 무한 재귀 호출 하기 때문에 탈출 조건을 반드시 만들어야한다.
- 지키지 않을시 스택 오버플로 에러 발생
- 반복문을 사용하는 것보다 직관적으로 이해하기 쉬울 때만 한정적으로 사용하자.
12.7.3 중첩 함수
- 함수 내부에 정의된 함수를 중첩함수 또는 내부 함수라고 한다.
- 자신을 포함하는 외부 함수를 돕는 헬퍼 함수의 역할을 한다.
- 호이스팅으로 인한 혼란이 발생될 수 있으니 if문이나 for문 등 코드 블록에서 함수를 정의하는 것은 권장하지 않는다.
12.7.4 콜백 함수
function repeat1(n) {
for (let i = 0; i < n; i++) console.log(i);
}
repeat1(5) // 0 1 2 3 4
function repeat2(n) {
for (let i = 0; i < n; i++) {
if (i % 2) console.log(i);
}
}
repeat2(5) // 1 3
- 위의 함수 두개는 반복한다는 일은 공통적이지만 반복해야하는 일은 다르다.
- 함수의 일부분만 다르기때문에 공통적인 부분은 미리 정의하고, 변경되는 로직은 따로 생성하여 인수로 전달하자.
- (아래) 개선된 예제
- 콜백 함수
- 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수
- 고차 함수
- 매개변수를 통해 외부에서 콜백 함수를 전달받은 함수
- 콜백 함수
// 반복만 하는 함수 생성 , 고차 함수
function repeat(n, f) {
for (let i = 0; i < n; i++) {
f(i);
}
}
let logAll = function(i) {
console.log(i);
}
repeat(5, logAll); // 0 1 2 3 4
logAll = function(i) {
if (i % 2) console.log(i);
} // 콜백 함수
repeat(5, logAll) // 1 3
- 중첩 함수는 고정되어 있어 자유도가 떨어지지만 콜백 함수는 교체가 가능하다.
- 콜백 함수가 고차 함수 내부에서만 호출되면 리터럴 형태로 인수로 전달할 수 있다. (아래)
- 이 방식은 고차 함수가 호출될 때마다 콜백 함수가 생성된다.
repeat(5, function (i) {
if (i % 2) console.log(i);
})
- 그러나 콜백함수가 다른 함수에서도 호출하거나, 자주 호출된다면 따로 정의하는게 좋다.
- 단 한번 생성후 재사용 된다.
- 콜백 함수는 비동기 처리에 활용되는 중요한 패턴이다.
- 또한 배열 고차 함수에서도 중요하게 활용된다.
12.7.5 순수 함수와 비순수 함수
- 순수 함수
- 외부 상태에 의존하지 않고 변경하지도 않는 함수
- 동일한 인수가 전달되면 항상 동일한 값을 반환한다.
- 최소 하나의 인수를 전달받는다.
- 인수의 불변성을 유지한다.
- 외부 상태에 의존하지 않고 변경하지도 않는 함수
let count = 0;
function increase(n) {
return ++n;
}
count = increase(count);
console.log(count); // 1
count = increase(count);
console.log(count); // 2
- 비순수 함수
- 외부 상태에 의존하거나 외부 상태를 변경하는 함수
- 인수를 전달받지 않고도 외부 상태를 직접 참조한다.
- 함수 내부에서 외부 상태를 변경하면 오류 추적이 어렵기 때문에 사용을 권장하지 않는다.
- 외부 상태에 의존하거나 외부 상태를 변경하는 함수
let count = 0;
function increase() {
return ++count;
}
increase();
console.log(count); // 1
increase();
console.log(count); // 2
728x90
반응형
'개발서적 > 모던 자바스크립트 Deep Dive' 카테고리의 다른 글
전역 변수의 문제점 (모던 자바스크립트 Deep Dive) (0) | 2024.04.16 |
---|---|
스코프 (모던 자바스크립트 Deep Dive) (0) | 2024.04.16 |
원시 값과 객체의 비교 (모던 자바스크립트 Deep Dive) (0) | 2024.04.16 |
객체 리터럴 (모던 자바스크립트 Deep Dive) (0) | 2024.04.16 |
타입 변환과 단축 평가 (모던 자바스크립트 Deep Dive) (0) | 2024.04.15 |