You don't know JS - this

이동창·2021년 10월 25일
0

this란 무엇인가?

어떤 함수를 호출하면 context가 만들어지는데,
여기에는 함수가 실행될 때 필요한 정보들이 담겨져있다.
this는 이 context 중 하나로, 실행되는 동안 이용가능하다.

오해 2가지

  1. this가 함수 그 자체를 가리킨다는 오해
  2. this가 함수의 스코프를 가리킨다는 오해

this는 함수 호출 당시 상황에 따라 context가 정해진다.
결국, 어떻게 호출했느냐에 따라 this 바인딩이 정해짐

호출부와 호출 스택

function baz() {
  // 호출 스택 baz
  // 호출부 전역 스코프 내부
  bar()
}

function bar() {
  // 호출 스택 baz -> bar
  // 호출부 baz
  foo()
}

function foo() {
  // 호출 스택 baz -> bar -> foo
  // 호출부 bar
  console.log("hi")
}

this 바인딩은 호출부와 연관되어 있기에, 이를 유심히 봐야함

바인딩 4가지 방법

  1. 기본 바인딩
function foo() {
  console.log(this.a)
}

var a = 2

foo(); //2

foo 실행 시 함수 내부의 this는 전역 객체를 가리키게 된다.

  1. 암시적 바인딩
function foo() {
  console.log(this.a)
}

var obj = {
  a: 2
  foo: foo
}

obj.foo() // 2

obj안에서 foo 함수를 프로퍼티로 참조하고 있고,
호출 시점에 obj 객체는 준비되어 있는 상태이므로, context가 this에 바인딩 된다.
따라서 this.a 는 obj.a가 됨

만약 객체 프로퍼티 참조가 체이닝이라면 가장 인접한 컨텍스트가 바인딩 됨

어떻게 호출하냐가 중요한 이유가 여기서 나온다.
다음 코드를 봐보자

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

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

var a = 1

var bar = obj.foo

bar(); // 1

bar는 obj의 foo를 참조하는 변수가 아닌, 그냥 foo를 직접 가르키는 레퍼런스임

  1. 명시적 바인딩
    call(), apply() 메서드로 직접 this에 바인딩 하는 방법이 있음
function foo() {
  console.log(this.a)
}

var obj = {
  a: 2
}

foo.call(obj) //2

위 예제처럼 호출시 call() 안에 바인딩할 객체를 넣어준다

  1. new 바인딩
    함수 앞에 new 연산자가 붙으면 일어나는 일
  • 새 객체가 만들어진다.
  • 그 객체에 Prototype과 함수의 Prototype이 연결된다.
  • 생성된 객체는 함수의 this에 바인딩된다. 그 상태로 함수 실행
  • 함수 실행 후 그 객체를 반환한다.
function foo(a) {
  this.a = a
}

// 새 객체 만들고, 그 객체와 foo.prototype 연결한 뒤, foo 실행 후 객체 반환
var bar = new foo(2) 
console.log(bar.a) // 2

바인딩 순서

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

안전한 바인딩

만약 this 바인딩이 다른 것에 잘못 되는 일을 막고 싶다면
Obejct.create(null) 과 같은 빈 객체를 만들어 바인딩 시켜버리는 것도 방법
이러면 뜻하지 않은 부수효과가 전역 객체에서 발생하지 않는다.

--- 최종 정리 ---

var bar = new foo() // this는 새로 생성된 객체이다
var bar = foo.call(obj2) // this는 obj2이다
var bar - obj1.foo(); // this는 obj1이다
var bar = foo() // this는 전역객체다 (엄격모드에서는 undefined)

화살표 함수

화살표 함수는 위의 규칙을 따르지 않고, 스코프를 통해 알아서 this 바인딩 한다.

function foo() {
  return (a) => {
    console.log(this.a) // 여기서 this는 foo()에서 가져온다
  }
}

var obj1 = {
  a: 2
}

var obj2 = {
  a: 3
}

var bar = foo.call(obj1)
bar.call(obj2) // 2

위를 보면 bar.call(obj2)를 함에도 불구하고
foo에서 화살표 함수의 this는 obj1를 인 것을 볼 수 있다.

즉, 함수 호출에서 넣은 obj1을 그대로 사용하는 것

0개의 댓글