[JavaScript] ES6에서의 함수와 this

Yongjin·2021년 9월 18일
2

JavaScript

목록 보기
2/5
post-thumbnail

들어가면서


JavaScript에는 다양한 함수의 표현식이 존재한다. 일반적인 함수의 표현식, 화살표 함수, 객체 안에서의 함수, 클래스 안에서의 함수 등 다양한 함수의 표현식이 존재한다. 평소에 표현식에 따른 차이가 존재한다고 알고 있기는 했지만, 이번 기회에 정확히 표현식에 따른 차이점을 정리해보려고 한다.

ES6 이전의 함수


ES6 이전까지 자바스크립트의 함수는 별다른 구분 없이 다양한 목적으로 사용되었다.
자바스크립트의 함수의 함수는 다음 세가지로 호출할 수 있다.

  • 일반적인 함수
  • new 연산자와 함께 호출하여 인스턴스를 생성하는 일반적인 함수
  • 객체에 바인딩되어 메서드
const a = function() {
  return 'a';
};
// 일반적인 함수
a();
// 생성자 함수로서
new a();
// 메서드
const obj = { a: a };
obj.a();

위의 코드처럼 ES6 이전의 모든 함수는 일반적인 함수로서 호출할 수 있는 것은 물론 생성자 함수로서도 호출할 수 있었다. 즉, 모든 함수는 callable이면서 constructor이다.
이로 인해, 함수의 호출 방식에 특별한 제약이 없고 생성자 함수로 호출하지 않아도 프로토타입 객체를 생성한다.
이를 해결하기 위헤 ES6에서는 함수를 사용 목적에 따라 다음의 세 가지로 구분했다.

ES6의 함수constructorprototypesuperarguments
일반 함수OOXO
메서드XXOO
화살표 함수XXXX

메서드(method)


ES6에서는 축약 표현으로 정의된 함수를 의미한다.

const obj = {
  a: 1,
  foo() {
    return this.a;
  },
  bar: function() { return this.x; }
}

위의 코드에서 축약된 표현으로 정의된 foo()는 메서드, 그렇지 않은 bar()는 일반 함수이다.
ES6에서 정의한 메서드는 인스턴스를 생성할 수 없는 non-constructor이다. 인스턴스를 생성할 수 없으므로, prototype 프로퍼티가 없고, 프로토타입도 생성하지 않는다.

obj.foo.hasOwnProperty('prototype');  // false
obj.bar.hasOwnProperty('prototype'); // true

ES6에서의 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯 [[HomeObject]]을 갖는다. 따라서, super 키워드를 사용할 수 있다.

super 참조는 내부 슬롯 [[HomeObject]]를 사용하여 상위 클래스의 메서드를 참조한다.

화살표 함수 (arrow function)


화살표 함수(arrow function)

  • function 키워드 대신 화살표(=>)를 사용하여 함수를 간략하게 정의한다.
  • 표현만 간략한 것이 아니라 내부 동작 또한 기존의 함수보다 간략하다.
  • 콜백 함수 내부에서 this가 전역 객체를 가리키는 문제를 해결하기 위한 대안으로 유용하다.

화살표 함수와 일반 함수의 차이

1. 화살표 함수는 인스턴스를 생성할 수 없는 non-constructor이다.

const arror() = () => {};
new arror(); // TypeError: fun is not a constructor
arror.hasOwnProperty('prototype'); // false

위의 코드에서 확인할 수 있듯이 화살표 함수는 인스턴스를 생성할 수 없으므로 prototype 프로퍼티가 없고 프로토타입도 생성하지 않는다.

2. 중복된 매개변수 이름을 사용할 수 없다.

// SyntaxError: Duplicate parameter name not allowed in this context
const arrow = (a, a) => a + a;

일반 함수와는 달리 화살표 함수에서는 중복된 매개변수 이름을 사용하면 에러가 발생한다.

3. 함수 자체의 this, arguments, super, new.target 바인딩을 갖지 않는다.

  • 화살표 함수 내부에서 this, arguments, super, new.target을 참조하면 스코프 체인을 통해 상위 스코프의 this, arguments, super, new.target을 참조한다.
  • 만약 화살표 함수가 중첩되어 있다면, 스코프 체인 상에서 가장 가까운 상위함수 중에 화살표 함수가 아닌 함수의 this, arguments, super, new.target을 참조한다.

this


화살표 함수와 일반 함수가 가장 구별되는 특징은 바로 this이다.
화살표 함수의 this는 일반 함수의 this와 다르게 동작하는데, 이것은 콜백 함수 내부의 this와 외부 함수의 this가 다르다는 문제를 해결한다.

this바인딩은 함수의 호출 방식, 즉 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다.

먼저, 아래의 코드를 살펴보자.

const value = 1;

const obj = {
  value: 100,
  foo() {
    console.log('this: ', this);  // { value: 100, foo: f }
    setTimeout(function () {
      console.log('callback's this: ', this);  // windw
      console.log('callback's this.value: ', this.value);  // 1
    }, 100);
  }
}
obj.foo();

위의 코드에서 setTimeout의 콜백함수로 일반 함수가 호출되었다. 이 경우 콜백 함수 내부에는 전역 객체가 바인딩된다.

이처럼 일반 함수로 호출된 함수 내부의 this에는 전역 객체가 바인딩 된다.

중첩 함수 또는 콜백 함수는 보통 외부 함수를 돕는 헬퍼 함수의 역할을 주로 한다. 하지만 위의 예시처럼 this에 전역 객체가 바인딩된다면 헬퍼 함수로서 동작하기가 힘들다.
따라서, 중첩 함수나 콜백 함수의 this 바인딩과 상위 메서드의 this 바인딩을 일치시켜야 한다.

this 바인딩을 일치시키기 위한 방법은 다음과 같다.

1. this 바인딩을 위한 변수 선언

const value = 1;

const obj = {
  value: 100,
  foo() {
    // this 바인딩을 that에 할당한다.
    const that = this;
    // 콜백 함수에서 this 대신 that을 참조한다.
    setTimeout(function () {
      console.log(that.value);  // 100
    }, 100);
  }
}

obj.foo();

2. 메서드 활용

JavasScript에서는 this를 명시적으로 바인딩할 수 있는 apply, call, bind 메서드를 제공한다.

const value = 1;

const obj = {
  value: 100,
  foo() {
    // 콜백 함수에 this를 바인딩한다.
    setTimeout(function () {
      console.log(this.value);  // 100
    }.bind(this), 100);
  }
};

obj.foo();

3. 화살표 함수

const value = 1;

const obj = {
  value: 100,
  foo() {
    // 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
    setTimeout(() => { console.log(this.value), 100);  // 100
  }
};

obj.foo();

화살표 함수는 함수 자체의 this 바인딩을 갖지않고, 상위 스코프의 this를 참조한다. =lexical scope

이처럼, this 바인딩의 문제를 해결하기 위해 화살표 함수를 사용하면 가장 간단히 문제를 해결할 수 있다.

하지만 화살표 함수가 상위 스코프의 this를 참조한다는 점이 문제를 일으킬 때도 있다. 아래의 경우를 보자.

const obj = {
  value: 100,
  fun: () => console.log(this.value), // undefined
};
obj.fun();

위의 코드의 경우 fun 프로퍼티에 할당된 화살표 함수 내부의 this는 메서드를 호출한 객체인 obj를 가리키지 않고 상위 스코프인 전역의 this가 가리키는 전역 객체를 가리키게 된다.
따라서 화살표 함수로 메서드를 정의하는 것은 바람직하지않다.
메서드를 정의할 때는 아래의 경우처럼 ES6 메서드 축약 표현으로 정의한 ES6 메서드를 사용하는 것이 좋다.

const obj = {
  value: 100,
  fun() {
    console.log(this.value), // 100
  }
};

this와 마찬가지로 super, arguments 키워드 또한 함수 자체의 super, arguments 바인딩도 갖지 않고, 상위 스코프의 super, arguments를 참조한다.

references


MDN - 화살표 함수
MDN - this
모던자바스크립트 - Arrow function

profile
성장하는 개발자

0개의 댓글