화살표 함수와 this
화살표 함수의 축약 문법과 외부 this를 캡처하는 특성을 익히고 메서드·생성자처럼 피해야 할 위치를 구분합니다.
우리는 6장 자바스크립트 심화 I에서 this가 호출 방식에 따라 동적으로 바인딩된다는 점을 학습했습니다.
이 특성은 콜백/중첩 함수에서 혼란을 만들었고, bind, call, apply, self = this 같은 우회 패턴을 자주 요구했습니다.
ES2015(ES6)에서 도입된 화살표 함수(Arrow Function)는 이러한 this 바인딩 문제를 근본적으로 해결하는 동시에, 함수를 선언하는 문법을 훨씬 간결하게 만들어 주었습니다.
화살표 함수는 모던 자바스크립트 코드에서 거의 필수적으로 사용되는 기능이 되었으므로, 정확한 이해가 중요합니다.
이번 장에서는 화살표 함수 기본 문법, this 바인딩 차이, 일반 함수와의 사용 시나리오를 비교해 정리합니다.
일반 함수의 this는 호출 방식에 따라 바뀌지만, 화살표 함수는 바깥 렉시컬 this를 그대로 사용합니다. 콜백과 메서드 경계를 구분해야 합니다.
- 화살표 함수의 기본 문법
화살표 함수는 function 키워드 대신 화살표 = 를 사용하여 함수를 정의하는 간결한 문법입니다. 자바스크립트 심화 I
- 화살표 함수와 this 바인딩의 차이점
this를 새로 바인딩하지 않는 동작이 화살표 함수를 사용하는 핵심 이유입니다. 화살표 함수(Arrow Function)
- 화살표 함수를 사용하지 말아야 할 경우
화살표 함수는 매우 편리하지만, 모든 상황에서 기존 함수를 대체할 수 있는 것은 아닙니다. 매개변수가 하나일 때
- 화살표 함수와 this 기준
정확성 일반 함수의 this는 호출 방식이 결정하고, 화살표 함수의 this는 선언된 바깥 스코프가 결정합니다. 비용 화살표 함수는 자신만의 this를 만들지 않아 콜백에서 발생하는 this 바인딩 혼란을 줄입니다. 확장성 화살표 함수는 매개변수 => 표현식 또는 매개변수 => { 함수 본문 }으로 작성하며, 매개변수가 하나면 괄호를 생략할 수 있습니다. 예외 매개변수가 없을 때: 빈 괄호를 사용해야 합니다.
- 정확성 일반 함수의 this
호출 방식이 결정하고, 화살표 함수의 this는 선언된 바깥 스코프가 결정합니다.
- 비용 화살표 함수
자신만의 this를 만들지 않아 콜백에서 발생하는 this 바인딩 혼란을 줄입니다.
- 확장성 화살표 함수
매개변수 => 표현식 또는 매개변수 => { 함수 본문 }으로 작성하며, 매개변수가 하나면 괄호를 생략할 수 있습니다.
- 예외 매개변수가 없을 때
빈 괄호를 사용해야 합니다.
화살표 함수의 기본 문법
화살표 함수는 function 키워드 대신 화살표 =>를 사용하여 함수를 정의하는 간결한 문법입니다.
기본 형태
매개변수 => 표현식 또는 { 함수 본문 }
// 기존 함수 표현식
const add = function(a, b) {
return a + b;
};
// 화살표 함수 (기본 형태)
const addArrow = (a, b) => a + b; // 중괄호와 return이 생략됨
console.log(addArrow(2, 3)); // 결과: 5다양한 형태
매개변수가 하나일 때: 괄호를 생략할 수 있습니다.
const greet = name => `Hello, ${name}!`;
console.log(greet("Alice")); // 결과: Hello, Alice!매개변수가 없을 때: 빈 괄호를 사용해야 합니다.
const sayHi = () => "Hi there!";
console.log(sayHi()); // 결과: Hi there!함수 본문이 여러 줄일 때: 중괄호를 사용하고 명시적으로 return 키워드를 사용해야 합니다.
const calculateSum = (numbers) => {
let sum = 0;
for (let num of numbers) {
sum += num;
}
return sum;
};
console.log(calculateSum([1, 2, 3, 4, 5])); // 결과: 15객체 리터럴을 반환할 때: 소괄호로 감싸야 합니다.
(중괄호가 함수 본문으로 해석되는 것을 방지)
const createUser = (name, age) => ({ name: name, age: age });
console.log(createUser("Bob", 28)); // 결과: { name: 'Bob', age: 28 }화살표 함수와 this 바인딩의 차이점
화살표 함수와 this에서는 값의 변화, 실행 순서, 화면 반응을 함께 봅니다.
this는 함수가 어떻게 호출됐는지로 정해지지만 화살표 함수는 자신만의 this를 만들지 않고 바깥 this를 캡처한다.
| 상황 | 일반 함수 | 화살표 함수 | 선택 기준 |
|---|---|---|---|
| Object method | 호출 객체가 this | 바깥 this 사용 | 객체 상태를 읽으면 일반 함수 |
| Callback | 호출자가 this를 정함 | 상위 this 유지 | this 손실을 막고 싶으면 화살표 |
| Event listener | 리스너 대상이 this가 될 수 있음 | 외부 상태를 this로 사용 | DOM 요소가 필요한지 확인 |
| Constructor | new와 prototype 사용 가능 | new 불가 | 인스턴스 생성이면 일반 함수/class |
| arguments | 자신의 arguments 있음 | 바깥 arguments 참조 | 가변 인자는 rest parameter 사용 |
이것이 화살표 함수를 사용하는 가장 큰 이유이자 가장 중요한 특징입니다.
기존의 function 키워드로 선언된 함수는 호출 방식에 따라 this가 동적으로 결정됩니다.
하지만 화살표 함수는 this를 자신이 선언될 때의 상위 스코프(Lexical Scope)에서 가져옵니다.
즉, this가 정적으로 바인딩됩니다.
기존 함수의 this
const person = {
name: "Minjun",
// 1. 메서드로서 호출: `.` 앞의 객체(person)가 `this`가 됨
sayHello: function() {
console.log(`Hello, my name is ${this.name}.`);
},
// 2. 중첩 함수에서 `this` 문제 발생
greetDelayed: function() {
// 기존 함수의 `this` (person)를 유지하기 위한 꼼수: `self = this`
const self = this;
setTimeout(function() { // setTimeout의 콜백 함수는 일반 함수이므로 `this`가 전역 객체(window)를 가리킴
console.log(`Delayed Hello, my name is ${self.name}. (using self)`);
// console.log(`Delayed Hello, my name is ${this.name}. (using this - WRONG!)`);
}, 500);
}
};
person.sayHello(); // 결과: Hello, my name is Minjun.
person.greetDelayed(); // 결과: Delayed Hello, my name is Minjun. (using self)
// WRONG! 줄은 undefined가 출력되거나 에러 발생위 예시에서 setTimeout 내부의 function() { ... }은 일반 함수이므로 this가 window (브라우저) 또는 undefined (엄격 모드)를 가리킵니다.
그래서 this.name은 제대로 된 값을 가져오지 못합니다.
화살표 함수의 this
화살표 함수는 자신이 정의될 때의 상위 스코프의 this를 그대로 가져옵니다.
this 바인딩을 생성하지 않습니다.
const personArrow = {
name: "Seulgi",
sayHello: () => { // 화살표 함수가 최상위 스코프에 정의되면 `this`는 `window`를 가리킴
console.log(`Hello, my name is ${this.name}. (WRONG! - window or undefined)`);
},
greetDelayed: function() { // 이 함수는 일반 함수이므로 `this`는 `personArrow`를 가리킴
// 화살표 함수는 자신의 상위 스코프(greetDelayed)의 `this`를 가져옴
setTimeout(() => {
console.log(`Delayed Hello, my name is ${this.name}. (using arrow function)`);
}, 500);
}
};
personArrow.sayHello(); // 결과: Hello, my name is . (window.name이 없거나 undefined)
// 주의: 객체의 메서드로 최상위 화살표 함수 사용은 권장되지 않음
personArrow.greetDelayed(); // 결과: Delayed Hello, my name is Seulgi. (using arrow function)personArrow.greetDelayed 내부의 setTimeout 콜백은 화살표 함수로 정의되었습니다.
이 화살표 함수는 자신이 선언된 greetDelayed 함수 스코프의 this 값을 그대로 가져옵니다.
greetDelayed는 personArrow 객체의 메서드로 호출되었으므로, greetDelayed 내부의 this는 personArrow를 가리킵니다.
따라서 화살표 함수 내부의 this도 personArrow를 가리키게 되어 원하는 대로 동작합니다.
핵심: 화살표 함수는 this 바인딩에 대한 문제를 해결해주는 강력한 도구입니다.
특히 콜백 함수에서 this 컨텍스트를 유지해야 할 때 유용합니다.
화살표 함수를 사용하지 말아야 할 경우
화살표 함수는 매우 편리하지만, 모든 상황에서 기존 함수를 대체할 수 있는 것은 아닙니다.
this 바인딩 방식의 차이 때문에 특정 상황에서는 기존 함수를 사용해야 합니다.
객체의 메서드를 정의할 때 (최상위 메서드):
위 personArrow.sayHello() 예시에서 보았듯이, 객체 리터럴의 최상위 메서드를 화살표 함수로 정의하면 this가 객체 자신이 아닌 전역 객체를 가리키게 되어 의도치 않은 결과를 초래합니다.
const myObject = {
value: 10,
// BAD: 화살표 함수는 this가 상위 스코프 (window/global)를 가리킴
getValueArrow: () => {
console.log(this.value); // undefined (또는 window.value)
},
// GOOD: 일반 함수는 `.` 호출 시 myObject를 this로 바인딩
getValueNormal: function() {
console.log(this.value); // 10
}
};
myObject.getValueArrow();
myObject.getValueNormal();객체의 메서드는 항상 일반 함수(또는 ES6의 메서드 축약형 method() { ... })로 정의해야 합니다.
생성자 함수 (Constructor):
화살표 함수는 prototype 프로퍼티를 가지지 않으며, new 키워드와 함께 호출될 수 없습니다.
new와 함께 호출하면 TypeError가 발생합니다.
// BAD: 화살표 함수는 생성자로 사용할 수 없음
// const MyClass = () => { this.value = 1; };
// const instance = new MyClass(); // TypeError: MyClass is not a constructor
// GOOD: 생성자는 일반 함수 또는 class 키워드로 정의
function MyClass(value) {
this.value = value;
}
const instance = new MyClass(10);arguments 객체에 접근해야 할 때:
화살표 함수는 arguments 객체를 바인딩하지 않습니다.
대신 상위 스코프의 arguments를 참조합니다.
기존 함수는 자신에게 전달된 인자들을 arguments 객체로 접근할 수 있습니다.
(ES6에서는 ...rest 매개변수 사용이 더 권장되긴 합니다.)
function sumAllNormal() {
console.log(arguments); // [1, 2, 3]
return Array.from(arguments).reduce((a, b) => a + b);
}
console.log(sumAllNormal(1, 2, 3)); // 결과: 6
const sumAllArrow = () => {
// console.log(arguments); // ReferenceError: arguments is not defined (상위 스코프에 arguments 없으면)
// 화살표 함수에서는 ...rest 매개변수를 사용해야 함
// return [...arguments].reduce((a, b) => a + b); // Error!
};
// console.log(sumAllArrow(1, 2, 3));addEventListener 등 이벤트 핸들러:
특정 DOM 요소에 이벤트 리스너를 붙일 때, this는 기본적으로 이벤트를 발생시킨 요소를 가리킵니다.
화살표 함수를 사용하면 이 this 바인딩이 깨져 의도치 않은 결과를 초래할 수 있습니다.
<button id="myButton">클릭하세요</button>
<script>
const button = document.getElementById('myButton');
// GOOD: 일반 함수는 this가 버튼 요소 자체를 가리킴
button.addEventListener('click', function() {
console.log("일반 함수 this:", this.id); // 결과: myButton
});
// BAD: 화살표 함수는 this가 상위 스코프 (window)를 가리킴
button.addEventListener('click', () => {
console.log("화살표 함수 this:", this.id); // 결과: undefined (window.id는 없음)
});
</script>DOM 이벤트처럼 호출 주체가 중요한 코드는 아래 흐름으로 일반 함수와 화살표 함수를 구분해 선택합니다.
이벤트 리스너는 DOM 요소 자체가 필요한지, 바깥 객체 상태가 필요한지에 따라 일반 함수와 화살표 함수를 다르게 고른다.
| 필요한 것 | 일반 함수가 맞는 경우 | 화살표 함수가 맞는 경우 | 확인할 API |
|---|---|---|---|
| Clicked element | this가 리스너 대상이어야 함 | event.currentTarget을 쓰면 대체 가능 | currentTarget |
| Outer state | this를 따로 bind해야 함 | 상위 this를 그대로 캡처 | class field, closure |
| Delegation | 부모에서 이벤트를 받고 event.target으로 실제 요소 확인 | 화살표도 가능 | target.closest |
| Remove listener | 같은 함수 참조가 필요 | 익명 화살표는 제거 어려움 | handler 변수 보관 |
| Readability | DOM this가 의미를 드러냄 | 외부 상태 접근이 선명 | 팀 규칙을 통일 |
화살표 함수·this 정리
정리 단계에서는 문법 간결성보다 this 바인딩이 코드 유지보수성에 주는 효과를 우선 확인합니다.
화살표 함수는 바깥 this를 고정하고, 일반 함수는 호출한 주체에 따라 this가 결정됩니다.
- 화살표 함수 선택 기준표 함수
형태별 this 결정 방식 화살표 함수는 바깥 this를 고정하고, 일반 함수는 호출한 주체에 따라 this가 결정됩니다.
- Arrow Function 바깥 문맥
유지해야 할 때 콜백과 배열 메서드처럼 호출 주체보다
- 주변 스코프
더 중요할 때 잘 맞습니다.
이번 장에서는 ES2015(ES6)의 핵심 기능인 화살표 함수(Arrow Function)의 간결한 문법과, 특히 개발자들을 혼란스럽게 했던 this 바인딩 문제를 어떻게 해결하는지에 대해 심도 있게 학습했습니다.
화살표 함수는 선언 위치의 상위 스코프 this를 그대로 사용하는 렉시컬 this 바인딩을 갖습니다.
콜백이나 중첩 함수에서 this가 바뀌면 안 되는 경우에 이 특성이 도움이 됩니다.
하지만 화살표 함수가 모든 상황에서 기존 함수의 대안이 될 수는 없다는 점도 중요하게 다루었습니다.
객체의 메서드, 생성자 함수, arguments 객체에 접근해야 할 때, 그리고 이벤트 리스너의 this 바인딩이 중요한 경우에는 여전히 기존 함수(또는 class 문법의 메서드)를 사용해야 합니다.
화살표 함수는 간결하지만 생성자 함수나 자체 this가 필요한 메서드에는 맞지 않습니다.
짧은 콜백에는 적합하고, 객체 메서드의 this가 중요한 경우에는 일반 함수와 비교해 선택해야 합니다.
마지막으로 함수 형태를 고를 때는 짧은 문법보다 this가 어디서 정해져야 하는지를 먼저 묻는 습관이 중요합니다.
화살표 함수는 자신의 this를 만들지 않으므로 콜백에는 유리하지만 메서드와 생성자 역할에는 맞지 않는 경우가 많다.
| 질문 | 화살표 사용 | 일반 함수 사용 | 주의점 |
|---|---|---|---|
| 상위 this가 필요한가 | 예, 캡처가 목적 | 아니오 | this 값이 어디서 오는지 기록 |
| 호출 객체가 this여야 하나 | 부적합 | obj.method에 적합 | 메서드 분리 대입 주의 |
| new로 호출할 함수인가 | 불가 | 생성자/class 사용 | prototype 없음 |
| arguments가 필요한가 | rest parameter 사용 | arguments 사용 가능 | 명시적 인자를 선호 |
| 이벤트 대상이 필요한가 | event.currentTarget 권장 | this로 DOM 대상 접근 가능 | 팀 규칙을 정함 |
화살표 함수는 자신만의 this, arguments, prototype을 만들지 않아 바깥 this 유지에는 강하지만 메서드나 생성자에는 맞지 않는다.
| 특징 | 의미 | 좋은 사용처 | 피할 자리 |
|---|---|---|---|
| No own this | 바깥 lexical this 사용 | setTimeout, Promise callback | 객체 메서드 |
| No arguments | 자기 arguments 없음 | rest parameter로 대체 | arguments가 필요한 함수 |
| No prototype | 생성자로 사용할 수 없음 | 짧은 콜백 | new 호출 대상 |
| Expression body | 짧은 반환 표현 가능 | map/filter 콜백 | 복잡한 로직을 한 줄에 압축 |
| Readability | 콜백 의도 명확 | 상위 상태 유지 | this 출처가 오히려 숨겨짐 |
화살표 함수는 짧게 쓰는 함수 문법이 아니라 자신만의 this를 만들지 않고 바깥 lexical scope의 this를 그대로 쓴다.
| 상황 | 결과 | 좋은 패턴 | 위험 패턴 |
|---|---|---|---|
| Class callback | 인스턴스 this 유지 | field arrow handler | 불필요한 bind 반복 |
| Array callback | this보다 값 변환이 핵심 | map/filter arrow | 복잡한 side effect 숨김 |
| Object literal | 객체가 this가 되지 않음 | 일반 method syntax 사용 | arrow method로 this undefined |
| DOM listener | 바깥 this 유지 | event.currentTarget 사용 | this가 DOM일 거라 기대 |
| Nested function | 바깥 문맥 보존 | 짧은 내부 콜백 | 어느 this인지 추적 불가 |