본문 바로가기
C.W.K.
Stream
Lesson 03 of 05 · published

화살표 함수와 일반 함수: 차이는 this에서 시작돼

~10 min · functions, arrow-functions, this, scope

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"두 문법은 취향만 다른 것이 아니야. this를 누가 정하는지에 대해 서로 반대 결정을 해."

일반 함수의 this는 호출 방식이 정해

function으로 만든 함수의 this는 함수를 어떻게 호출했는지에 따라 달라져. obj.method()로 부르면 obj가 되고, 평범한 함수로 떼어 호출하면 엄격 모드에서 undefined가 될 수 있어. call, apply, bind로 명시적으로 정할 수도 있지.

이 성질은 객체의 메서드처럼 수신자가 중요할 때 유용해. 같은 함수라도 어느 객체의 동작으로 호출하는지에 따라 this가 달라져야 하니까. 대신 메서드를 값처럼 떼어 콜백에 넘기면 원래 수신자를 잃기 쉽다.

화살표 함수는 바깥 this를 붙잡아

화살표 함수는 자기 this를 만들지 않아. 정의된 자리의 바깥 this를 그대로 사용하고, 이후 call로 다른 값을 넘겨도 바뀌지 않아. 콜백 안에서 둘러싼 클래스 인스턴스나 객체 문맥을 유지하고 싶을 때 자연스러워.

반대로 객체 리터럴의 메서드를 화살표 함수로 만들면 obj.method()라고 불러도 this가 obj가 되지 않아. ‘메서드처럼 생겼으니 객체를 가리키겠지’라는 기대가 틀릴 수 있어. 수신자가 this가 되어야 하는 메서드는 일반 메서드 문법을 써.

클래스 메서드를 콜백으로 넘길 때

클래스의 일반 메서드는 prototype에 하나만 있고 인스턴스들이 공유해. 하지만 setTimeout(instance.method)처럼 넘기면 호출할 때 인스턴스가 사라질 수 있어. 화살표 함수로 감싸 () => instance.method()라고 하거나, 미리 bind하거나, 클래스 필드를 화살표 함수로 선언하는 방법이 있어.

화살표 필드는 인스턴스마다 새 함수가 만들어진다는 비용이 있어. 대부분 앱에서는 작지만 인스턴스가 아주 많다면 고려할 수 있어. 습관 하나로 모두 해결하려 하지 말고 호출 방식과 객체 수명을 보고 골라.

수신 객체가 this여야 하면 일반 메서드, 바깥 문맥을 붙잡아야 하면 화살표 함수. 스타일보다 호출 의미로 선택해.

피파의 고백

‘왜 여기서 this가 undefined지?’를 한 번 깊게 겪고 나면 화살표 함수가 예쁜 문법이 아니라 문맥 보존 장치로 보여. 버그를 고칠 때는 함수가 어디에 쓰였는지보다 어디서 어떤 형태로 만들어졌고 어떻게 호출됐는지를 함께 봐야 해.

Code

Dynamic vs lexical `this`·typescript
// Declared 함수 — `this` 는 caller 가 결정.
function announce() {
  console.log(this);   // announce 호출 방식에 따라
}

const obj = { name: 'Pippa', announce };
obj.announce();          // `this` 가 obj
const fn = obj.announce;
fn();                    // strict mode 에서 `this` undefined
announce.call({ name: 'Other' });  // `this` 가 { name: 'Other' }

// Arrow 함수 — `this` 가 정의된 곳에서 캡처.
const announceArrow = () => {
  console.log(this);   // 정의 시점에 둘러싼 scope 의 `this`
};

const obj2 = { name: 'Pippa', announceArrow };
obj2.announceArrow();    // `this` 가 obj2 아님 — arrow 정의를 둘러쌌던 무엇이든
Class method 와 callback — canonical 고통·typescript
// Class-method gotcha 와 두 가지 해결.

class Counter {
  count = 0;

  // 일반 method — `this` 는 caller 에 의존.
  increment() {
    this.count++;
  }

  // Arrow-field — 생성 시점에 `this` 캡처.
  incrementArrow = () => {
    this.count++;
  };
}

const c = new Counter();

// Callback 으로 전달:
setTimeout(c.increment, 100);          // ❌ `this` 잃음
setTimeout(() => c.increment(), 100);  // ✅ arrow 로 감쌈
setTimeout(c.incrementArrow, 100);     // ✅ arrow-field 가 `this` 보존
setTimeout(c.increment.bind(c), 100);  // ✅ 명시 bind, 더 옛 스타일

// Trade-off: arrow-field 가 instance 마다 새 함수 할당.
// 일반 method 는 prototype 에 살아, 모든 instance 가 공유.
// 대부분 앱엔 이 할당이 무관; instance 가 많은
// class 엔 중요할 수.

External links

Exercise

타이머 콜백을 세 방식으로 구현해. 일반 메서드를 그대로 넘기는 방식, 화살표 콜백에서 호출하는 방식, 메서드 자체를 화살표 필드로 만드는 방식을 비교해.
Hint
메서드를 그대로 넘기면 호출 문맥을 잃고, 화살표 콜백으로 감싸면 바깥 인스턴스를 보존해. 화살표 필드는 생성 시점부터 인스턴스를 잡는 차이가 있어.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.