해당 포스팅은 위키북스의 모던 자바스크립트 Deep Dive라는 책을 독학하며 기록하는 글입니다.

객체지향 프로그래밍에서 살펴보았듯이 객체는 상태를 나타내는 프로퍼티와 동작을 나타내는 메서드를 하나의 논리적인 단위로 묶은 복합적인 자료구조이다.

this의 필요성

동작을 나타내는 메서드는 자신이 속한 객체의 상태인 프로퍼티를 참조하고 변경할 수 있어야 하는데 이때, 우선적으로 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.

이를 위해 자바스크립트는 this라는 특수한 식별자를 제공하는데 this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수다. this를 통해 자신이 소한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.

this는 자바스크립트 엔진에 의해 암묵적으로 생성되며 코드 어디서든 참조될 수 있다. 하지만 this가 가리키는 값, this의 바인딩은 함수 호출 방식에 의해 동적으로 결정되므로 무작정 썼다가는 생각과는 다른 결과가 나올 수 있다.

함수의 호출 방식

그럼 함수의 호출 방식에 어떠한 방식이 있는지 먼저 살펴보자.

  1. 일반 함수 호출
    일반 함수 호출은 일반적으로 우리가 사용하는 방식이다.
const f = function() {
  console.log('f')l
}

f();
  1. 메서드 호출
    메서드 호출은 객체의 프로퍼티로 있는 함수를 점 표기법과 같이 호출했을 때를 의미한다.
const Person = {
  move() {
    console.log('움직인다.');
  }
}

Person.move();
  1. 생성자 함수 호출
    생성자 함수 호출은 말그대로 함수에 new키워드를 붙혀 인스턴스를 생성하는 함수를 호출하는 방식을 말한다.
const Person = {
  move() {
    console.log('움직인다.');
  }
}

const p = new Person();
  1. Function.prototype.apply/call/bind 메서드에 의한 간접 호출
    apply, call, bind는 모든 함수가 가지고 있는 Function.prototype 메서드로 함수를 간접적으로 호출하는 메서드들이다.
const f = function() {
  console.log('f')l
}

// 각각 전달해야 하는 인자가 있지만 아래에서 설명하겠다.
f.apply(); f.call(); f.bind();

함수 호출 방식에 따른 this의 바인딩

  1. 일반 함수 호출
    어떠한 함수를 일반 함수로 호출하면 해당 함수에 있는 this에는 무조건 전역 객체가 바인딩된다.
    함수를 생성자 함수로 사용하기 위해 만들었다고 해도 호출할 때 new를 붙히지 않고 일반 함수로서 호출했다면 생성자 함수 내의 this에도 전역 객체가 바인딩된다.

  2. 메서드 호출
    함수를 메서드로써 호출했다면 해당 함수를 호출한 객체가 this에 바인딩 된다.

  3. 생성자 함수 호출
    함수를 새 인스턴스를 만들기 위해 생성자 함수로써 호출했다면 해당 함수 내의 this에는 미래에 만들어질 인스턴스가 바인딩된다.

  4. Function.prototype.apply/call 메서드에 의한 간접 호출
    apply와 call 메서드는 인자로 함수를 호출할 때 this에 바인딩할 객체를 직접 받아 실행시킨다. 두 번째 인자로는 arguments를 받는데 이는 선택적이다.
    apply와 call의 차이는 두 번째 인자인 arguments를 받을 때 apply는 배열의 형태로 받고, call은 각 argument를 ,로 구분해서 받는다.

function f() {
  return this;
}

const person = {
  name: 'Han'
}

f.apply(person, [1, 2, 3]);
f.call(person, 1, 2, 3);
  1. Function.prototype.bind 메서드에 의한 간접 호출
    bind 메서드는 인자로 함수를 호출할 때 this에 바인딩할 객체를 전달만 해준다. 따라서 this에 바인딩할 객체를 전달하고 실행시키기 위해서는 다음과 같이 명시적으로 실행해줘야 한다.
function f() {
  return this;
}

const person = {
  name: 'Han'
}

f.bind(person)();

추가. bind 메서드의 활용

다음 예제를 보자.

const person = {
  name: 'Han',
  f(callback) {
    setTimeout(callback, 1000);
  }
}

person.f(function() {
  console.log(this.name);
}

브라우저의 콘솔창에는 어떤 값이 출력될까?? 바로 빈 문자열이다.
왜냐하면 함수 f를 메서드로 호출했지만 그 안에 있는 setTimeout함수 자체는 일반 함수로서 실행이 되었기 때문에 그 안에 this에는 전역객체인 window(브라우저에서의 전역객체는 window)가 바인딩되었고 window.name에는 기본값으로 ''이 할당되어 있기 때문이다.

이때 bind 메서드를 사용하면 내부함수와 외부함수의 this를 일치시킬 수 있다.

const person = {
  name: 'Han',
  f(callback) {
    setTimeout(callback.bind(this), 1000);
  }
}

person.f(function() {
  console.log(this.name);
}

이 경우 setTimeout이 실행되기 이전까지 this에 바인딩되어 있는 person객체를 callback함수를 실행시킬 때 callback함수의 내부 this에 바인딩할 값으로 전달해줬기 때문에 정상적으로 'Han'이 출력된다.

profile
I Will be Relaxed Person

0개의 댓글