this binding Error

GI JUNG·2022년 10월 3일
2

javascript

목록 보기
1/12
post-thumbnail

this binding error는 일반함수말고 화살표 함수를 왜 쓰는지 의문을 가져 this binding error를 알아보게 되었다.
이를 공부하면서 정리한 것은 아래와 같다.

  1. 일반 함수는 호출 시 객체를 통한 호출이 아니면 전역 객체가 binding된다.
  2. arrow function안에 있는 this는 arrow function을 감싸고 있는 가장 가까운 일반함수와 binding된다.
  3. class내부의 method는 proptype으로 생성된다.
  4. arrow function은 constructor로 동작할 수 없다.

일단, this binding error가 어떤 경우에서 일어나는지 살펴보자

🍀 this binding error 예시

const counter = {
  value: 1,
  increase() {
    this.value++;
  },
};

counter.increase();
console.log(counter.value);   // 1️⃣
const { increase } = counter;
increase();
console.log(counter.value);   // 2️⃣

나는 처음에 이 코드를 봤을 때 1️⃣은 2, 2️⃣는 2에서 증가한 3이라고 예상했지만, 각각 2, 2가 된다.
1️⃣과 2️⃣를 살펴보면 차이점은 객체를 통한 호출이다.

다른 예시로 다음은 생성자 함수를 통한 this binding error이다.

function Counter() {
  this.value = 1;
  this.increase = function () {
    this.value++;
  };
}

const counter = new Counter();
counter.increase();
console.log(counter.value);   // 1️⃣
const { increase } = counter;
increase();   // 3️⃣
console.log(counter.value);   // 2️⃣

위의 예시에서도 각각 2, 2가 나온다. 왜 이러한 일이 발생하는 것일까? 🤔
이유는 3️⃣에 있다. 3️⃣은 counter에서 increase함수를 받아온 것으로 this가 안에있다.
3️⃣에서 increase 함수가 호출되는 당시 this를 binding해야 하는데 this가 선언된 내부에서 찾는 것이 아닌 전역에서 찾는다.
즉, 이를 정리하면 아래와 같다.

📒 객체를 통하지 않은 호출은 호출될 당시의 scope에서 this를 binding하므로 browser환경에서는 window와 binding이 된다.

🍀 this & arrow function

위에서 increase함수를 일반함수로 작성했는데 arrow function으로 작성하면 어떨까?
아래 예시에서는 decrease를 arrow function으로 작성하였다.

const counter = {
  value: 1,
  increase() {
    this.value++;
  },
  decrease: () => this.value--
};

counter.increase();
console.log(counter.value);   // 1️⃣
const { decrease } = counter;
decrease();
console.log(counter.value);   // 2️⃣

실행 결과를 살펴보면 2️⃣는 증가하지 않은 2라는 값을 가진다. arrow function도 일반함수와 동일하게 동작한다고 생각할 수도 있지만
아래 예시를 보면 arrow function이 일반함수와 다르게 동작함을 알 수 있다.

function Counter() {
  this.value = 1;
  this.increase = function () {
    this.value++;
  };
  this.decrease = () => this.value--;
}

const counter = new Counter();
counter.increase();
console.log(counter.value);   // 1️⃣
const { decrease } = counter;
decrease(); 
console.log(counter.value);   // 2️⃣

이제 출력되는 counter.value의 값을 살펴보면 1이 출력되며 객체를 통한 호출이 아니어도 감소시키고자 한 의도와 맞게 코드가 돌아감을 확인할 수 있다.

왜 그럴까?🤔

내가 느낀 일반함수와 화살표함수의 가장 큰 차이점이라 생각한다.

📒 일반함수와 달리 arrow function은 호출 시에도 자신이 선언된 부분에서 자신을 감싸고 있는 가장 가까운 일반함수와 binding된다.

이해가 안 될 수 있어 추가적인 설명을 하자면, arrow function을 감싸고 있는 가장 가까운 함수는 Counter.이다.
위에서 객체 안에 넣은 arrow function은 arrow function을 object가 감싸고 있다. object는 일반함수와 다르기 때문에 선언된 부분에서 위로 가장 가까운 일반함수를 찾다가 window 객체와 바인딩되는 일이 일어나는 것이다.
하지만, Counter를 생성자 함수로 만들면 arrow function으로 선언한 decrease는 Counter함수(가장 가까운 일반함수)와 this가 binding(this = Counter)되어 감소시킬 수 있는 것이다.

🍀 this binding error 해결하기

내가 공부하면서 찾은 방법으로는 총 3가지가 있다.

  1. 일반함수가 아닌 arrow function 사용하기
  2. bind method 사용하기
  3. closure 이용하기

1. arrow function 사용하기

class Counter {
  constructor(value){
    this.value = value;
  }

  start(){
    setInterval(function(){
      this.value++;
      console.log(this);
      console.log(`value: ${this.value}`);
    }, 1000)
  }
}

const counter = new Counter(0);
counter.start();

실행결과

실행결과를 보면 this -> window, value -> NaN가 된다. setInterval 실행 시 전역환경에서 실행되어this가 window이며 window는 value라는 속성이 없어 window.value에 접근했을 때 undefined가 나오게된다. 그리고 undefined++ ++연산자를 통해 NaN이라는 값이 나오는 것이다.
이제 arrow function을 이용하여 해결해 보자!!

class Counter {
  ...//
  start(){
  	setInterval(() => {
      this.value++;
      console.log(this);
      console.log(`value: ${this.value}`);
    }, 1000);
  }
}
...//

실행결과

이제 내가 원하던 방향으로 this가 바인딩이 되고 value값이 1초마다 1씩 증가하는 것을 알 수 있다.
이 부분에서 헷갈렸던 것이 있는데 start function이 arrow function을 감싸고 있는


정리


참고


일반함수의 경우 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.

화살표 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다. 동적으로 결정되는 일반 함수와는 달리 화살표 함수의 this 언제나 상위 스코프의 this를 가리킨다. 이를 Lexical this라 한다

For a function expression, this changes based on the context a function is called in. For arrow functions, this is based on the enclosing lexical scope. Arrow functions follow normal variable lookup. They look for this in the current scope, and if not found, they move to the enclosing scope.

화살표 함수를 남용하면 안 되는 이유

profile
step by step

0개의 댓글