You don't know JS - this (2)

박춘화·2021년 9월 12일
0

You don't know JS

목록 보기
3/4
post-thumbnail

this는 호출부에 의해 결정된다! 😮

앞 글에서 우리는 'this'가 함수를 호출하는 방법에 의해 결정된다는 결론을 얻었다. 이에 관해서 You don't know JS에서는 호출부에 따라 'this'가 바인딩 되는 규칙을 다음과 같이 4가지로 정의했다.

  • 기본 바인딩
  • 암시적 바인딩
  • 명시적 바인딩
  • new 바인딩

기본 바인딩 🙄

함수가 평범하게 호출될 때 나머지 규칙이 적용되지 않는다면 기본적으로 적용되는 규칙이다.

function foo() {
  console.log(this.a)
}

var a = 2

foo() // 2

var a = 2와 같이 전역 스코프에 변수를 선언하면 동일한 이름의 전역 객체 프로퍼티가 생성된다. 따라서, 함수 호출 시에 기본 바인딩 규칙에 따라 'this'는 전역 객체를 가리키게 된다.
단, 엄격 모드Strict Mode에서는 전역 객체가 기본 바인딩 대상에서 제외된다. 그래서 이 경우에 'this'는 undefined가 된다. (여기서 엄격 모드는 foo 내부에서의 엄격 모드를 가리킨다. foo가 호출되는 호출부의 엄격 모드와는 상관이 없다.)

function foo() {
  console.log(this.a)
}

var a = 2

(function() {
  'use strict'
  foo() // 2
})()

암시적 바인딩 🙄

호출부에 함수를 참조하는 컨텍스트 객체가 있다면 'this'는 그 객체를 가리키게 된다.

function foo() {
  console.log(this.a)
}

var obj = {
  a : 2,
  foo : foo
}

obj.foo() // 2

객체 obj는 함수 foo를 프로퍼티로 참조하고 있다. 그러므로 foo 호출 시에 foo에 대한 컨텍스트 객체로 obj가 지정되어 암시적 바인딩 규칙에 따라 'this'는 객체 obj를 가리키게 된다.

명시적 바인딩 🙄

암시적 바인딩 규칙을 따르려면 함수를 참조하는 객체에 프로퍼티로 추가해줘야하는 번거로움이 있다. 또한, 암시적 바인딩은 객체의 깊이에 따라 바인딩이 사라지는 암시적 소실이라는 문제도 생긴다. 그러므로 함수를 객체에 프로퍼티로 추가하지 않고도 함수가 해당 객체를 'this'로 바인딩할 수 있는 방법이 필요한 때이다.
이와 같이 함수가 'this'로 참조하는 객체를 명확히 지정하는 방식을 명시적 바인딩이라 하며, 자바스크립트를 이를 위해 call()apply()라는 두가지 메서드를 지원한다.

function foo() {
  console.log(this.a)
}

var obj = {
  a : 2,
}

foo.call(obj) // 2

call()apply() 메서드는 받은 인자를 'this'로 바인딩한 뒤에 함수를 호출한다. 두 메서드는 동일한 역할을 하지만 call()은 인자로 인수 목록을 받으며, apply()은 인자로 인수 배열을 받는다.
비슷한 역할을 하는 메서드로 bind() 메서드가 있다. bind() 메서드는 첫 번째 인자로 받는 값을 'this'에 바인딩하고, 그 이후의 값들을 함수의 인자로 넘겨주는 새로운 함수를 반환한다.

new 바인딩 🙄

new 바인딩 규칙은 new 표현식으로 생성된 객체를 'this'에 바인딩한다. (new 표현식은 전통적인 클래스 지향 언어의 생성자 함수를 호출하는 방식처럼 보이지만, 그 내부는 전혀 다르다. 이는 자바스크립트가 프로토타입 기반 언어이기 때문이다.)

function foo(a) {
  this.a = a
}

var bar = new foo(2)
console.log(bar.a) // 2

앞에 new를 붙여 foo()를 호출했고 새로 생성된 객체는 foo 호출 시 this에 바인딩 된다.

바인딩 규칙 순서 😏

위에서 설명한 바인딩 규칙들이 중첩 적용된 경우에는 어떤 바인딩 규칙이 먼저 적용될까?
함수의 호출부를 기반으로 다음과 같은 우선순위에 따라 바인딩 규칙이 적용된다.

  1. new로 호출했다면 새로 생성된 객체로 바인딩 된다.(new 바인딩)
  2. call이나 apply 또는 bind로 호출됐다면 주어진 객체로 바인딩 된다.(명시적 바인딩)
  3. 호출의 주체인 컨텍스트 객체로 호출됐다면 바로 이 콘텍스트 객체로 바인딩 된다.(암시적 바인딩)
  4. 기본 바인딩에서 엄격 모드는 undefined, 비엄격 모드는 전역 객체로 바인딩 된다.(기본 바인딩)

후기 😎

이상으로 You don't know JS에서 언급한 'this'의 4가지 바인딩 규칙들에 대해 알아봤다. 규칙에는 언제나 그렇듯 예외가 있으며, 이와 관련된 내용은 책에 자세하게 설명되어 있으니 책을 꼭 읽어보는 것을 추천한다.

profile
함께 성장하는 개발자

0개의 댓글