JS

[JS] 화살표 함수 => 와 this의 차이점

coco030030 2025. 4. 10. 17:38

 

let user = {
  name: "a",
  normalFunc: function() {
    setTimeout(function(){
      console.log("일반 함수 호출 구조 : " + this.name);
    }, 500);
  },
  arrowFunc: function() {
    setTimeout(() => {
      console.log("화살표 함수가 정의되어있는 곳 : " + this.name);
    }, 1000);
  },
};

user.normalFunc();
user.arrowFunc();

코드 핵심 - setTimeout과 this의 관계

이 코드는 일반 함수와 화살표 함수의 this 바인딩 차이를 setTimeout 컨텍스트에서 보여줌.

1. 일반 함수의 this 동작

normalFunc: function() {
  setTimeout(function(){ // 일반 함수 사용
    console.log("일반 함수 호출 구조 : " + this.name);
  }, 500);
}
  • 결과: "일반 함수 호출 구조 : undefined"
  • 이유:
    • normalFuncuser 객체의 메서드
    • setTimeout은 콜백 함수를 내부적으로 다른 실행 컨텍스트에서 실행함
    • 일반 함수는 자신만의 this를 생성하는데, setTimeout에서 실행될 때는 전역 객체(브라우저에서는 window) 또는 undefined를 가리킴
    • 따라서 this.nameundefined가 됨
    • 중요: setTimeout이 콜백 함수를 실행할 때 this 바인딩을 "마음대로" 바꿔버림

2. 화살표 함수의 this 동작

arrowFunc: function() {
  setTimeout(() => { // 화살표 함수 사용
    console.log("화살표 함수가 정의되어있는 곳 : " + this.name);
  }, 1000);
}
  • 결과: "화살표 함수가 정의되어있는 곳 : a"
  • 이유:
    • arrowFuncuser 객체의 메서드
    • 화살표 함수는 매우 중요한 특성이 있음: 자신만의 this를 생성하지 않음
    • 화살표 함수는 "렉시컬 스코프(Lexical Scope)"의 this를 그대로 상속받음
    • 렉시컬 스코프란 "화살표 함수가 정의된 위치의 환경"을 의미함
    • arrowFunc 메서드 내에서 화살표 함수가 정의되었으므로, 화살표 함수 내 thisarrowFuncthis(즉, user 객체)를 그대로 물려받음
    • 따라서 화살표 함수 안에서 this.nameuser.name과 동일한 "a"를 정확히 가져옴

핵심 정리 - this의 동작 방식

  1. 일반 함수의 this:
    • 호출 방식에 따라 this가 동적으로 결정됨
    • obj.method() 형태로 호출하면 thisobj를 가리킴
    • 하지만 setTimeout 같은 환경에서는 this 바인딩이 달라짐
    • 콜백 함수로 전달된 일반 함수 내부의 this는 전역 객체 또는 undefined를 가리킴
  2. 화살표 함수의 this:
    • 선언된 위치(렉시컬 스코프)에 따라 this가 정적으로 결정됨
    • 화살표 함수는 this를 "태어난 곳"(정의된 위치)에서 물려받음
    • setTimeout에서도 원래 환경의 this를 그대로 유지함
    • 따라서 콜백 함수로 전달되어도 외부 스코프의 this를 기억함

실무 응용 방법

  1. 콜백에서 상위 스코프 this 접근 방법:
  2. // 방법 1: 화살표 함수 사용 (권장) setTimeout(() => { console.log(this.name); }, 1000); // 방법 2: 변수에 this 저장 const self = this; setTimeout(function(){ console.log(self.name); }, 1000); // 방법 3: bind 메서드 사용 setTimeout(function(){ console.log(this.name); }.bind(this), 1000);
  3. 언제 무엇을 사용할지:
    • 콜백에서 상위 스코프의 this가 필요하면 → 화살표 함수
    • 콜백 내에서 자체적인 this 바인딩이 필요하면 → 일반 함수
    • 메서드 정의 시에는 this 바인딩이 필요하므로 → 일반 함수 사용 권장

이 코드는 자바스크립트에서 가장 많이 혼동되는 개념인 this의 동작을 명확하게 보여주는 훌륭한 예제입니다.


이미지 속 예제 코드 다시 보기:

let user = {
  name: "a",
  normalFunc: function() {
    setTimeout(function(){
      console.log("일반 함수 호출 구조 : " + this.name);
    }, 500);
  },
  arrowFunc: function() {
    setTimeout(() => {
      console.log("화살표 함수가 정의되어있는 곳 : " + this.name);
    }, 1000);
  },
};

user.normalFunc();
user.arrowFunc();

이 코드의 "핵심" setTimeout 안에서 this 가 어떻게 동작하는지 보여주는 것! 특히 일반 함수 ( function )화살표 함수 ( => ) 를 비교해서 this 의 차이점을 강조하고 있어.

1. normalFunc : "일반 함수" 에서 setTimeout 사용했을 때 ⏰

normalFunc: function() {
    setTimeout(function(){ // 👈 일반 함수 (function 키워드)
      console.log("일반 함수 호출 구조 : " + this.name);
    }, 500);
  },
  • normalFuncuser 객체의 "메서드" 야. function 키워드로 만들어진 "일반 함수" 메서드이지.
  • setTimeout(function(){ ... }, 500);: setTimeout"일정 시간 (500ms, 0.5초) 후에 "특정 함수" 를 실행해줘!" 라고 예약하는 기능이야. 여기서는 setTimeout"익명 함수 (이름 없는 함수)" 를 "콜백 함수" 로 전달했어. 콜백 함수는 "나중에 실행될 함수" 라고 생각하면 돼.
  • console.log("일반 함수 호출 구조 : " + this.name);: 이 부분이 핵심! setTimeout 에 전달된 "콜백 함수 (익명 함수)" 안에서 this.name 을 출력하려고 하고 있어. 여기서 this 가 뭘 가리키느냐가 중요한 거야!

결과: user.normalFunc() 를 실행하면, 약 0.5초 후에 콘솔에 "일반 함수 호출 구조 : undefined" 라고 출력될 거야.

undefined 가 나올까? 🤔

  • setTimeout 이 "콜백 함수" 를 실행할 때, this 값을 "제대로 설정해주지 않기 때문" 이야. 정확히 말하면, setTimeout 이 콜백 함수를 "전역 객체 (window)" 또는 "undefined" 환경에서 실행시켜 버려. user 객체의 normalFunc 메서드 안에서 setTimeout 을 호출했지만, setTimeout 의 콜백 함수 안의 thisuser 객체를 "가리키지 않게 되는" 거야.
  • 주석 //this : 본인을 호출한 객체를 참조 는 "일반적인 객체 메서드" 의 this 동작 방식을 설명한 것인데, setTimeout 콜백 함수에서는 "예외" 가 적용되는 거야. setTimeoutthis 를 멋대로 바꿔버리는 "나쁜 녀석" 이라고 생각해도 괜찮아! 😅

2. arrowFunc : "화살표 함수" 에서 setTimeout 사용했을 때 🏹

arrowFunc: function() {
    setTimeout(() => { // 👈 화살표 함수 (=>)
      console.log("화살표 함수가 정의되어있는 곳 : " + this.name);
    }, 1000);
  },
  • arrowFuncuser 객체의 "메서드" 야. 하지만 setTimeout 에 전달하는 콜백 함수를 "화살표 함수 ( => )" 로 사용했다는 점이 normalFunc 와 달라.
  • setTimeout(() => { ... }, 1000);: setTimeout 에 콜백 함수로 "화살표 함수 (익명 화살표 함수)" 를 전달했어.
  • console.log("화살표 함수가 정의되어있는 곳 : " + this.name);: 핵심! 이번에는 setTimeout 콜백 함수가 "화살표 함수" 이기 때문에, this 동작 방식이 달라져!

결과: user.arrowFunc() 를 실행하면, 약 1초 후에 콘솔에 "화살표 함수가 정의되어있는 곳 : a" 라고 출력될 거야.

"a" 가 제대로 나올까? ✨

  • 화살표 함수는 this 를 "특별하게" 처리하기 때문 이야! 화살표 함수는 this 를 "자신이 정의된 곳의 스코프에서 "상속" 받아와." 이걸 "렉시컬 스코프 (Lexical Scope)" 라고 불러. 말이 좀 어렵지만, 쉽게 말하면 "화살표 함수가 "태어난 곳" (정의된 곳) 의 "환경" 을 그대로 가져온다" 는 뜻이야.
  • arrowFunc 메서드 안에서 화살표 함수가 정의되었지? arrowFunc 메서드 안에서 this 는 "당연히" user 객체를 가리켜. (객체 메서드니까!) 화살표 함수는 이 arrowFunc 메서드의 this (즉, user 객체) 를 "그대로 물려받아서" 사용하는 거야. 그래서 화살표 함수 안에서 this.nameuser.name 과 똑같은 값인 "a" 를 제대로 가져올 수 있는 거지.
  • 주석 //arrowFunc : this가 user라는 객체////화살표 함수가 정의된 위치 : arrowFunc()의 this 가 바로 이 점을 설명하고 있는 거야. 화살표 함수 안의 thisarrowFuncthis ( user 객체) 와 "똑같다" 는 것을 강조하는 거지.

핵심 정리! 🔑

  • setTimeout 콜백 함수에서 this 가 헷갈리는 이유: setTimeout 은 콜백 함수의 this 를 "마음대로" 바꿔버리기 때문! (전역 객체 또는 undefined 로)
  • 일반 함수 ( function ) 콜백: setTimeout 안에서 thisuser 객체를 "가리키지 않아서" this.nameundefined 가 됨.
  • 화살표 함수 ( => ) 콜백: 화살표 함수는 this 를 "렉시컬 스코프에서 상속" 받기 때문에, setTimeout 안에서도 arrowFuncthis ( user 객체) 를 "그대로 유지" 해서 this.name"a" 를 제대로 출력함.