JavaScript

js-this-thumb.png

This란

객체지향 프로그래밍에서 객체는 상태를 나타내는 프로퍼티와 동작을 나타내는 메서드가 하나의 논리적인 단위로 묶여진 자료구조다. 메서드가 자신이 속한 객체의 프로퍼티를 참조/변경하려면 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.

자바스크립트는 이를 위해 this 라는 특수한 식별자를 제공한다. this자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 참조 변수다. this가 참조하는 대상은 ‘함수가 호출되는 상황’에 따라 바뀐다. 자바스크립트의 this는 일단 기본적으로 함수가 호출될 때 결정되며, 보통 자신을 호출한 주체의 정보가 담기게 된다. 함수가 호출되는 상황은 크게 4가지가 있다.

1. 함수로서 호출

함수는 그 자체로 혼자서 독립적인 기능을 수행하는 경우 어딘가에 종속되지 않기 때문에, 함수는 호출한 주체 또한 없다. this는 자신을 호출한 주체의 정보를 담는다는 이야기를 했었는데, 함수 자체가 호출주체 없이 독립적으로 존재하면 this는 전역객체를 가리키게 된다.

function myFunction() {
  return this;
}
console.log(myFunction()); // Window

Strict Mode

Strict Mode (엄격 모드)에서는 함수 내 this 의 기본 바인딩이 없기 때문에, undefined 가 출력된다.

"use strict";

function myFunction() {
  return this;
}
console.log(myFunction()); // undefined

화살표 함수

  • 일반 함수는 호출 시 함수가 어떻게 호출 되는지에 따라 this가 가리킬 객체가 동적으로 결정된다. 반면, 화살표 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다.
  • 화살표 함수에서 this 는 항상 상위 스코프의 this 를 가리킨다.
  • 화살표 함수는 아래 4번과 같은 call, apply, bind 메서드로 this를 변경할 수 없다.
var Person = function (name, age) {
  this.name = name;
  this.age = age;

  this.say1 = function () {
    setTimeout(function () {
      console.log(this.name + " is " + this.age + " years old");
    }, 1000);
  };

  this.say2 = function () {
    setTimeout(() => {
      console.log(this.name + " is " + this.age + " years old");
    }, 1000);
  };
};

var Alex = new Person("Alex", 23);

Alex.say1(); // 'Alex is 23 years old'
Alex.say2(); // 'undefined is undefined years old'

2. 메서드로서 호출

메서드는 자신을 호출한 대상에 종속되어 있다. 호출한 대상은 쉽게말해 메서드 왼쪽에 명시된 객체라고 생각하면 된다. 어떤 객체에 종속된다는 메서드의 특징은 this에도 그대로 적용이 된다. 메서드로 호출된 this는 부모 객체를 기준으로 바인딩되기 때문이다.

var person = {
  firstName: "John",
  lastName: "Doe",
  fullName: function () {
    return this.firstName + " " + this.lastName;
  },
};

person.fullName(); //"John Doe"

3. new를 이용한 생성자 호출

생성자 함수는 어떤 공통된 성질을 갖는 객체들을 생성하는데 쓰이는 함수이다. 인스턴스를 찍어내는 일종의 ‘틀’ 같은 역할을 하는 것이다. new 키워드에서 this는 새롭게 생성된 인스턴스 객체를 의미한다.

function Person(name) {
  this.name = name;
}

var kim = new Person("kim");
var lee = new Person("lee");

console.log(kim.name); //kim
console.log(lee.name); //lee

4. call , apply , bind를 통한 호출 ( 명시적 바인딩 )

앞서 말한 this는 어떠한 규칙에 의해 자체적으로 적용되었다. 하지만, call이나 apply 메서드를 사용하면 this를 별도의 대상으로 지정할 수 있게 된다. call, apply, bind 호출에서는 첫번째 인자로 들어가는 값이 바로 this이다.

call & apply

const obj = { name: "Tom" };

const say = function (city) {
  console.log(`My name is ${this.name}. I live in ${city}.`);
};

say("Seoul"); // My name is . I live in Seoul.
say.call(obj, "Seoul"); // My name is Tom. I live in Seoul.
say.apply(obj, ["Seoul"]); // My name is Tom. I live in Seoul.

say("soeul")this에 어떤 기본 바인딩이 되어있지 않으므로 this는 window 객체이다.

say.call(obj, "Seoul")say.apply(obj, ["Seoul"])thisobj로 명시적으로 바인딩했다.

call 메서드는 첫 번째 인자(this를 대체할 값)를 제외하고, 함수에 필요한 인자를 직접 입력하는 방식이다.

이와 달리, apply 메서드는 두 번째 인자부터 모두 배열로 넘겨줘야 한다.

bind

const obj = { name: "Tom" };

const say = function (city) {
  console.log(`My name is ${this.name}. I live in ${city}.`);
};

const boundSay = say.bind(obj);

boundSay("Seoul"); // My name is Tom. I live in Seoul.

bind 메서드는 함수를 바로 실행하지 않는 대신 bound 함수를 반환한다. bound 함수는 bind 메서드로 this 에 바인딩한 값을 저장하고 있다 .

정리

함수 호출 방식 this 바인딩
일반 함수 호출 전역 객체
메서드 호출 메서드를 호출한 객체
생성자 함수 호출 생성자 함수가 (미래에) 생성할 인스턴스
apply, call, bind 메서드에 의한 간접 호출 각 메서드에 첫 번째 인수로 전달한 객체

Reference

Leave a comment