C.W.K.
Stream
Lesson 03 of 05 · published

Arrow vs Declared: 진짜 차이

~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
"Arrow 함수와 declared 함수는 스타일 variant 아냐. `this` 에 대해 다른 결정 하고, 그 결정은 영구적."

결정적 차이

`function` 선언의 `this` 는 dynamic. `this` 의 값은 함수 호출 방식으로 결정: method 로 (obj.fn() → `this` 가 `obj`), plain 함수로 (fn() → strict mode 에서 `this` 가 undefined), fn.call(other) 통해 (→ `this` 가 `other`), new fn() 통해 (→ `this` 가 새 instance).

Arrow 함수의 `this` 는 lexical. 정의 시점에 둘러싼 scope 에서 `this` 캡처. 한 번 캡처되면 절대 안 바뀜 — arrow 함수에 .call(other) 호출해도 `this` 에 아무것도 안 함.

그게 전체 규칙. 다른 모든 arrow-vs-declared 토론이 그것의 downstream.

각각 손 뻗을 때

Arrow 함수: callback, class method 안 event handler, inline helper, `this` 가 쓴 줄에서 의미한 거 의미하길 원하는 어디든. Modern TypeScript 의 90%+ case.

함수 선언 / 표현식: `this` 가 object 여야 하는 object method, class method (이미 자동으로 this-binding 의미 얻음), constructor, generator 함수. 그리고 진짜로 `this` 가 rebindable 하길 원하는 드문 case.

Class-method gotcha

Class 가 둘 다 섞어. Class method 는 내부적으로 함수 선언이라, `this` 가 dynamic. 즉 obj.method 를 callback 으로 전달하면 `this` 잃어:

class Counter {
  count = 0;
  increment() { this.count++ }
}
const c = new Counter();
setTimeout(c.increment, 1000);  // ❌ 호출 시 `this` undefined
setTimeout(() => c.increment(), 1000);  // ✅ arrow 가 `c` 보존

두 흔한 해결: 호출을 arrow 로 감싸기 (위), 또는 method 를 arrow-함수 field 로 정의 (increment = () => { this.count++ } — 생성 시점에 `this` 캡처). 두 번째는 비용 있어 — prototype 의 하나 대신 instance 마다 새 함수 — 근데 binding 이슈 제거.

이 토론의 90% 해결하는 단일 규칙: 함수가 callback 으로 전달되면 arrow (또는 arrow-field) 로 만들어. 직접 object 에 호출하는 method 면 일반 method 써. 모르면 — callback 엔 arrow 가 더 안전한 default.

피파의 고백

cwkPippa frontend 가 거의 어디서나 arrow 함수 써. 유일한 declared 함수는 class method (그리고 React 가 대부분 component 를 class 아니라 plain 함수로 render). Arrow-field 패턴은 method 가 non-React API 에 callback 으로 전달돼야 할 때 정확히 나타나. "이유 없으면 arrow 써" 규칙이 몇 년에 걸쳐 안정적이었어.

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

Timer class 써, start() method 가 setInterval 로 매초 this.tick() 호출. 3가지 방법 구현: (1) 일반 method 와 setInterval(this.tick, ...), (2) 일반 method 와 setInterval(() => this.tick(), ...), (3) tick 을 arrow-field. 어느 거 컴파일? 어느 거 runtime 에 옳게 행동? 왜?
Hint
버전 1 이 this 잃음. 버전 2 가 호출을 start 의 scope 에서 this 캡처하는 arrow 로 감쌈. 버전 3 이 class instance 레벨에서 this 캡처. 셋 다 실제 production 패턴 — 각각 시도, 차이 관찰, default 선택.

Progress

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

댓글 0

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

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