[Javascript] function
정의
function 은 JavaScript 에서 함수를 만드는 가장 기본적인 키워드. 호이스팅, this 동적 바인딩, arguments 객체, new 호출 등 arrow function 과 구분되는 고유한 특성을 가진다.
함수가 값 으로 다뤄지는 일급 함수 의 성질은 모든 형태에 공통.
사용 상황
| 상황 | 권장 형태 |
|---|---|
객체 메서드 (this 가 객체를 가리켜야 함) | function 선언/표현식 |
생성자 (new 와 함께) | function 선언/표현식 |
| prototype 메서드 추가 | function 표현식 |
| 재귀 함수 | 이름 있는 함수 표현식 또는 선언문 |
| 콜백, 이벤트 핸들러 (this 불필요) | 화살표 함수 권장 |
| 짧은 단방향 변환 | 화살표 함수 권장 |
| 호이스팅이 필요한 유틸 함수 | 함수 선언문 |
두 가지 모양
함수 선언 (Function Declaration)
function greet(name) {
return `Hello, ${name}`;
}
- 호이스팅 됨 (선언 전 호출 가능)
- 자기 이름
greet으로 자신을 참조 가능 (재귀)
함수 표현식 (Function Expression)
const greet = function (name) {
return `Hello, ${name}`;
};
// 이름 있는 함수 표현식 (named function expression)
const factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};
- 호이스팅 안 됨 (변수 선언만 호이스팅, 값은 평가 시점에 할당)
- 익명 또는 이름 있음, 이름은 내부에서만 보임
호이스팅의 차이
greet('A'); // 'Hello, A', 함수 선언은 호이스팅
function greet(name) {
return `Hello, ${name}`;
}
sayHi('B'); // TypeError: sayHi is not a function
const sayHi = function (name) {
return `Hi, ${name}`;
};
함수 선언은 함수 자체 가 스코프 최상단으로 끌어올려진다. 표현식은 const/let/var 가 호이스팅되는 규칙을 따른다.
호이스팅 동작 시각화
JavaScript 엔진은 실행 전 코드를 두 단계로 처리한다.
flowchart TD
A["소스 코드 파싱"]
A --> B["1단계: 선언 수집"]
B --> C["function 선언 전체를 스코프 최상단 등록"]
B --> D["var 선언은 이름만 등록, 값 = undefined"]
B --> E["let, const 는 TDZ 에 등록"]
A --> F["2단계: 실행"]
F --> G["라인 순서대로 값 할당 및 실행"]
C --> H["어디서든 호출 가능"]
D --> I["선언 전 접근 시 undefined"]
E --> J["선언 전 접근 시 ReferenceError"]
function 선언은 1단계에서 함수 객체 전체 가 등록되므로 어디서든 호출 가능. 호이스팅 참고.
this 동적 바인딩
일반 함수의 가장 큰 특징. 호출 방식에 따라 this 가 결정된다.
| 호출 방식 | this |
|---|---|
일반 호출 fn() | undefined (strict) 또는 전역 객체 |
메서드 호출 obj.fn() | obj |
fn.call(ctx), fn.apply(ctx) | ctx |
fn.bind(ctx)() | ctx |
new fn() | 새 인스턴스 |
| DOM 이벤트 리스너 | 이벤트 타겟 (보통) |
const obj = {
name: 'Alice',
greet() {
console.log(this.name); // 'Alice'
},
};
const fn = obj.greet;
fn(); // undefined, this 가 obj 가 아니다
이 “this 가 호출 시 결정” 특성이 arrow function 과 가장 큰 차이.
flowchart TD
Call["함수 호출"]
Call -->|"new fn()"| New["this = 새 인스턴스"]
Call -->|"fn.call(ctx)"| Bind["this = ctx"]
Call -->|"fn.bind(ctx)()"| Bind
Call -->|"obj.fn()"| Method["this = obj"]
Call -->|"fn() 직접 호출"| Direct["strict mode 여부"]
Direct -->|"strict"| Undef["this = undefined"]
Direct -->|"non-strict"| Global["this = 전역 객체"]
arguments 객체
일반 함수 안에는 arguments 라는 유사 배열 객체가 자동으로 존재.
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
sum(1, 2, 3); // 6
모던 코드에서는
...rest매개변수를 권장.arguments는 배열이 아니라 유사 배열이라.map등이 안 통한다.
new 호출
생성자로 사용 가능. this 는 새 인스턴스를 가리킨다.
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
return `Hello, ${this.name}`;
};
const p = new Person('Alice');
p.greet(); // 'Hello, Alice'
ES2015
class문법이 이를 깔끔하게 대체. 그래도 내부적으로는 같은 메커니즘.
프로토타입 체인
일반 함수는 prototype 속성을 가진다 (생성자로 쓰일 수 있어서).
function Animal() {}
Animal.prototype.eat = function () { /* ... */ };
const dog = new Animal();
dog.eat(); // 프로토타입 체인을 따라 찾음
arrow function 은 prototype 이 없어 new 호출 불가.
일반 함수 vs 화살표 함수 비교
| 특성 | function | () => {} |
|---|---|---|
this | 호출 시 결정 (동적) | 정의 시 결정 (lexical) |
arguments | ✓ | ✗ (rest 사용) |
new 호출 | ✓ (생성자 가능) | ✗ (TypeError) |
prototype | ✓ | ✗ |
| 호이스팅 (선언문) | ✓ | ✗ (표현식만 가능) |
| 짧은 문법 | △ | ✓ |
| 메서드로 적합 | ✓ | △ (this 가 lexical 이라서) |
자세한 화살표 함수 동작은 arrow function 참고.
고급 패턴
IIFE (즉시 실행 함수)
(function () {
// 즉시 실행, 전역 스코프 오염 방지
const secret = 'hidden';
console.log(secret);
})();
모듈 시스템이 없던 시절 스코프를 격리하는 패턴. 현재는 ES Modules 가 대체.
커링 (Currying)
function multiply(a) {
return function (b) {
return a * b;
};
}
const double = multiply(2);
double(5); // 10
double(10); // 20
인자를 하나씩 받는 함수 연쇄. 클로저 로 a 를 기억한다.
메모이제이션
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
const fib = memoize(function (n) {
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
});
언제 일반 함수를 쓰는가
- 객체의 메서드,
this가 객체를 가리켜야 함 - 생성자,
new와 함께 - prototype 메서드, 클래스 메서드
- 재귀 함수 (이름 있는 표현식 또는 선언문)
함정
1. 메서드를 변수에 꺼내면 this 잃음
const timer = {
tick: 0,
increment() {
this.tick++;
},
};
const inc = timer.increment;
inc(); // this = undefined (strict) 또는 전역
inc.call(timer); // this = timer (해결)
// 또는 bind 로 고정
const boundInc = timer.increment.bind(timer);
boundInc(); // 항상 timer
2. var 호이스팅과 함수 선언이 같은 이름
console.log(typeof foo); // 'function'
var foo = 'bar';
function foo() {}
// 함수 선언이 var 보다 우선 (호이스팅 순서)
3. arguments 는 배열이 아님
function f() {
arguments.map(x => x * 2); // TypeError: arguments.map is not a function
[...arguments].map(x => x * 2); // ✓ 배열로 변환
}
// 권장: rest 파라미터 사용
function g(...args) {
args.map(x => x * 2); // ✓
}
4. 화살표 함수를 메서드로 쓰면 this 가 전역
const obj = {
name: 'Alice',
greet: () => {
console.log(this.name); // undefined (lexical this = 전역)
},
};
obj.greet(); // 의도와 다름
관련 위키
이 글의 용어 (8개)
- [Javascript] 클로저javascript
- 정의 클로저 (Closure): 함수가 자신이 정의된 시점의 외부 변수들을 계속 참조할 수 있는 성질. 함수가 일급 객체 () 인 언어의 자연스러운 결과. 메커니즘은 + 함수 객…
- [Javascript] 호이스팅 (Hoisting)javascript
- 정의 호이스팅 (Hoisting) 은 JavaScript 엔진이 코드를 실행하기 전 바인딩 생성 단계 에서 스코프 내의 선언을 파악하고 등록하는 메커니즘이다. 마치 선언이 스코프…
- [Javascript] Arrow Functionjavascript
- 정의 화살표 함수 (arrow function) 는 ES2015 (ES6) 에서 도입된 함수 표현식. 모양. 더 짧은 문법뿐 아니라 가 lexical 로 바인딩 되는 본질적 차이…
- [Javascript] Lexical Environmentjavascript
- 정의 Lexical Environment (어휘적 환경) 는 ECMAScript 명세 (§9.1) 가 정의한 내부 객체. 함수가 호출될 때마다 새로 만들어지며, 이 호출의 변수들…
- [Javascript] Prototype Chainjavascript
- 정의 JavaScript 의 모든 객체는 (내부 슬롯) 을 가진다. 속성 lookup 시 자신에게 없으면 prototype chain 을 따라 올라가며 검색. 접근 함수의 pro…
- [Javascript] this 바인딩javascript
- 정의 JavaScript 의 는 호출 방식에 따라 결정 되는 동적 바인딩. 4 가지 호출 방식 + arrow function (lexical) 규칙. 5 가지 규칙 | # | 규…
- 고차 함수javascript
- 정의 고차 함수 (Higher-Order Function, HOF): 다음 조건 중 하나 이상을 만족하는 함수. 1. 함수를 인자로 받는다. 2. 함수를 반환한다. 특성이 있어야…
- 일급 함수javascript
- 정의 일급 함수 (First-Class Function): 함수를 숫자, 문자열, 객체처럼 값으로 다룰 수 있는 언어의 특성. 다음 세 가지가 모두 가능하면 그 언어의 함수는 일…
💬 댓글