Arrow Function에서의 this

contability·2024년 4월 8일
0

함수표현식으로 사용되는 arrow function에서의 this는 어떤 의미일까?

es5에서의 this


  1. 함수 실행시에는 전역(window) 객체를 가리킨다.
  2. method 실행시에는 method를 소유하고 있는 객체를 가리킨다.
  3. 생성자 실행시에는 새롭게 만들어진 객체를 가리킨다.

es6에서의 this


es6에서의 this는 Lexical this다.
자신을 둘러싼 환경의 this를 그대로 계승 받는다.

function objFunction() {
  console.log('Inside es5 function `objFunction`:', this.foo); // 13
  return {
    foo: 25,
    bar: () => console.log('Inside es6 arrow function `bar`:', this.foo) // 13
  };
}

objFunction.call({foo: 13}).bar();

es5에서는 method 호출시, this는 method를 소유하고 있는 객체를 가리킨다고 하였다.
따라서 es5라면 5번 라인의 this.foo는 25가 되어야겠지만 Arrow Function은 Lexical this를 갖기 때문에 13이 된다.
그러므로 call method를 통한 간접 실행이 일어날 때의 this 문맥을 그대로 계승하는 것이다.

이런 특징이 있어 편리할 때가 있다.
이는 대표적으로 콜백 함수 작성시에 유용하다. 콜백도 함수이기 때문이다.
es5에서는 함수 호출시 this가 전역 객체를 가리키는데 반해 arrow function은 상위 환경의 this를 계승받는다.

function Prefixer(prefix) {
  this.prefix = prefix;
}

Prefixer.prototype.prefixArray = function (arr) {
  return arr.map(x => `${this.prefix}  ${x}`);
};

const pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim']));  // ['Hi Lee', 'Hi Kim']

6번 라인의 콜백 함수에서 this는 상위 컨텍스트의 this를 그대로 계승받는다.
따라서 생성자 함수의 this를 따르게 되는 것이다.
즉, this가 가리키는 대상은 새롭게 생성된 prefixer 객체가 될 것이다.

하지만 es5 였다면 콜백 함수의 this는 전역 객체를 가리켰을 것이다.

es5에서도 미리 상위 컨텍스트의 this를 변수에 할당하는 등 여러가지 파훼법이 있었지만
arrow function으로 이 과정이 필요 없어졌다.
하지만 언제나 arrow function을 활용할 수 있는 것은 아니다.
써서는 안될 때가 있다.

arrow function을 써서는 안되는 경우


1. method

method를 arrow function으로 작성하면 문제가 생긴다.

const person = {
  name: 'Lee',
  sayHi: () => console.log(`Hi ${this.name}`)
};

person.sayHi(); // Hi undefined

이렇게 method를 arrow function으로 작성하게 된다면
arrow function에서의 this는 상위 환경의 this를 계승하므로 전역 객체를 가리키게 된다.
본래 의도는 person 객체를 가리키는 것이었으므로 결과적으로 부자연스러운 동작이 된다.
이럴 땐 전통적인 방법을 따라야한다.

const person = {
  name: 'Lee',
  sayHi: function(){
    console.log(`Hi ${this.name}`)
};

person.sayHi(); // Hi Lee

2. prototype

method를 prototype에 할당할 때도 arrow function을 사용하면 같은 문제가 발생한다.

const person = {
  name: 'Lee',
};

Object.prototype.sayHi = () => console.log(`Hi ${this.name}`);

person.sayHi(); // Hi undefined

마찬가지로 일반 함수로 사용해야 한다.

const person = {
  name: 'Lee',
};

Object.prototype.sayHi = function() {
  console.log(`Hi ${this.name}`);
};

person.sayHi(); // Hi Lee

3. 생성자

생성자 함수에서 this는 새롭게 만들어진 객체를 가리킨다고 했다.
하지만 arrow function은 생성자 함수로 쓰는 것 자체가 불가능하다.

const Foo = () => {
  console.log(this);
}

const foo = new Foo(); // TypeError: Foo is not a constructor

Foo는 생성자 함수가 아니라는 에러가 발생한다.
arrow function에는 prototype 프로퍼티가 없기 때문.

생성자 함수와 prototype의 관계는 이렇다.

js에서 함수를 정의하면 함수뿐만 아니라 프로토타입 객체도 함께 생성된다.
그리고 생성된 함수(왼쪽)의 prototype 속성은 이 프로토타입 객체를 가리키게 된다.
즉, 생성자 함수는 new 키워드를 통해 객체를 생성할 때 이 프로토타입 객체의 contructor를 사용하는 것이다.

하지만 arrow function은 이 prototype 속성 자에츨ㄹ 갖지 않으므로 일반 함수로 정의해주어야 한다.

4. addEventListener의 콜백 함수

addEventListener의 콜백함수로 arrow function을 정의하면 this는 전역 객체를 가리킨다.

const box = document.getElementById('box');

box.addEventListener('click', () => {
    console.log(this); //window    
});

arrow function은 부모 scope의 this를 계씅하므로 이 경우 this는 window를 가리키게 되는 것이다.
일반 함수로 사용하자.

const box = document.getElementById('box');

box.addEventListener('click', function() {
    console.log(this); //box
});

이제 this는 자식(box) scope로 rebound 되고 의도 했던대로 this는 box를 가리키게 된다.

0개의 댓글