Programming language/JavaScript

자바스크립트, 호출 패턴에 따른 this

iKay 2019. 4. 8. 22:54
반응형

 

this라는 매개변수는 객체지향 프로그래밍 관점에서 중요한 역할을 차지한다고 생각한다. JavaScript에서도 함수, 클래스를 호출할 때 this 매개변수를 사용할 수 있다. 그런데 호출하는 패턴에 따라 각기 this가 다르게 동작한다. 오늘은 각 경우에 따라 this가 어떻게 되는지 살펴본다.

this가 호출되는 패턴은 다음과 같이 네 가지가 있는 것 같다

  1. 메소드 호출 패턴
  2. 함수 호출 패턴
  3. 클래스 호출 패턴
  4. apply 호출 패턴

각각의 경우에 대해 this가 어떻게 다른지 자세히 살펴보자.

1. 메소드 호출 패턴

함수를 객체의 속성에 저장하는 경우 이 함수를 메소드라고 부른다. 이 경우는 메소드를 호출할 때, this는 메소드를 포함하고 있는 객체에 바인딩된다. 즉, 흔히 예상하는 것 처럼 this는 객체 자체가 된다.

var myObject1 = {
    value: 0,
    increment: function (inc) {
        this.value += typeof inc === 'number' ? inc: 1;
    }
};

myObject1.increment();
console.log(myObject1.value); // 1
myObject1.increment(2);
console.log(myObject1.value); // 3

이때 method(increment부분)를 다음과 같이 줄여서 표현할 수도 있고, 이 방식을 [1] Airbnb JavaScript Guide 에서도 권하고 있다.

var myObject1 = {
    value: 0,
    increment (inc) {
        this.value += typeof inc === 'number' ? inc: 1;
    }
};

하지만, 메소드 호출 패턴을 사용할 때, 화살표 함수(arrow function)의 this는 [2] this는 객체에 바인딩 되지 않는다. 사실 어느 곳에도 바인딩 되지 않는다. 이 점으로 보아, 메소드 내에서 this를 사용해야 하는 경우 화살표 함수로 메소드를 선언하지 않도록 주의해야 겠다.

var myObject2 = {
    value: 0,
    increment: (inc) => {
        this.value += typeof inc === 'number' ? inc: 1;
    }
};

myObject2.increment( );
console.log(myObject2.value); // 0
myObject2.increment(2);
console.log(myObject2.value); // 0

일반적으로 매소드 호출 패턴에서 this는 메소드를 호출할 때 객체의 속성들에 접근해 읽거나 쓰기를 할 때 사용된다. 하지만 화살표 함수 내에서 this는 메소드 호출시, 현재 context에 바인딩이 일어나서 this가 객체를 바인딩하지 않는다. 이것을 늦은 바인딩(lexical binding)이라 부른다. http://www.javascriptkit.com/javatutors/javascriptarrowfunctions2.shtml

2. 함수 호출 패턴

함수에서 this는 전역객체에 바인딩된다. 따라서 함수 호출 패턴에서는 this를 사용하지 말자! 또는 [3] bind( )를 사용하자!

var add = function add(a, b) {
    var sum = 0;
    this.sum = a + b;
    return this.sum;
}

console.log(add(3,4)); // 7
console.log(sum); // 7

아래 사진에서 볼 수 있듯이 VS Code debug로 봤을 때 this는 global 객체에 바인딩 됐음을 알 수 확인할 수 있다.

3. 클래스 호출 패턴

이 패턴은 흔히 객체지행 프로그래밍에서 사용되는 패턴이라 이해하는데 크게 어려움이 없을 것이다. this는 p1이라는 인스턴스가 생성될 때 그곳에 바인딩된다.

class Person {
    constructor(name='noName') {
        this.name = name;
    }
}

var p1 = new Person();
console.log(p1.name); // noName

4. apply 호출 패턴

아래 코드와 같이 apply는 객체 내부의 this를 바꾼다. 참고로, apply 메소드는 함수의 매개변수를 넘길 때 this도 함께 넘길 때 사용된다. (사실 apply가 어디에 주로 쓰이는 지는 모르겠다. 이 부분은 보완 필요!)

var myObject1 = {
    value: 0,
    increment(inc) {
        this.value += typeof inc === 'number' ? inc: 1;
    }
};

var changeThis = function () {
    this.value = -1;
}

myObject1.increment(); 
console.log(myObject1.value) // 1
changeThis.apply(myObject1, null);
console.log(myObject1.value); // -1

참고

[1] https://github.com/airbnb/javascript#es6-object-shorthand%5D(https://github.com/airbnb/javascript#es6-object-shorthand 
[2] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
[3] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
반응형