반응형

this 파헤치기


this 는 객체지향언어에서 클래스로 생성한 인스턴스의 의미한다. 하지만 JavaScript 에서의 this 는 어디서든 활용이 가능하다.

 

JavaScript 에서 this 는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정된다.
실행 컨텍스트는 함수를 호출할 때 생성되므로, this 는 함수를 호출할 때 결정된다고 이해할 수 있다.

전역공간에서의 this 는 전역 객체를 가르킨다. 전역 컨텍스트를 생성하는 주체가 바로 전역 객체이기 때문이다.
참고로 브라우저 환경에서 전역객체는 window이고, Node.js 환경에서는 global이다. 

var a = 1;
console.log(a);            // 1
console.log(window.a);     // 1
console.log(this.a);     // 1

위의 값은 모두 1이다. 그 이유는 JavaScript 의 모든 변수는 특정 객체의 프로퍼티로서 동적하기 때문이다.
사용자가 var 연산자를 이용해서 변수를 선언하더라도 실제 자바스크립트 엔진은 어떤 특정 객체의 프로퍼티로 인식하는 것이다.
특정 객체란 바로 실행 컨텍스트의 LexicalEnvironment 이다. 실행 컨텍스트는 변수를 수집하여 LexicalEnvironment 의 프로퍼티로 저장한다.

다시 이해하면 전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다는 것이다.

a 를 직접 호출해도 1이 나오는 이유는??
변수 a 에 접근하고자 하면 스코프 체인에서 a 를 검색하다가 가장 마지막에 도달하는 전역 스코프의 전역객체에서 해당 프로퍼티 a 를 발견하여

그 값을 반환하기 때문이다. 이해하기 어려다면 단순하게 window 를 생략했다고 이해해도 상관없다.

그렇다면 함수로 호출하는 것과 메서드로 호출하는 경우의 차이는 무엇일까? 2가지를 구분하는 차이는 독립성이다.
함수는 그 자체로서 독립적인 기능을 수행하지만, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행한다. 

var func = function(x) {
    console.log(this, x);
};

func(1);            // window { ... } 1

var obj = {
    method: func
};

obj.method(2);     // { method: f } 2

 

함수로서의 호출과 메서드로서의 호출의 구분은, 함수 앞에 점(.)의 여부이다. 앞에 점(.) 이 없으면 함수로서 호출한 것이고, 점(.)이 있으면 메서드로서 호출한 것이다.

 

 

메서드 내부에서의 this 
this 에는 호출한 주체에 대한 정보가 담기는 곳이다. 어떤 함수를 메서드로서 호출하는 경우 호출의 주체는 바로 함수명(프로퍼티명) 앞의 객체이다.
점(.) 표기법의 경우 마지막 점 앞에 명시된 객체가 곧 this 인 것이다.

함수로서 호출할 때 그 함수 내부에서의 this 
실행 컨텍스트를 활성화할 당시에 this 가 지정되지 않은 경우 this 는 전역객체를 바라본다. 따라서 함수에서의 this 는 전역객체를 가르키게 된다.
메서드의 내부 함수에서 this 를 우회하는 방법이 존재하긴 한다.

var obj = {
  outer: function() {
    console.log(this);            // { outer : f }
    var innerFunc1 = function() {
      console.log(this);          // Window { ... }
    };
    innerFunc1();

    var self = this;
    var innerFunc2 = function() {
      console.log(self);          // { outer : f }
    };
    innerFunc2();
  }
};
obj.outer();

 

위와 같이 작성하면, self 에는 객체 obj 가 출력된다.

this 를 바인딩하지 않고 사용하려면, 화살표 함수 (Arrow Function)을 사용하면 된다. 화살표 함수는 ES6에서 사용할 수 있다.

var obj = {
  outer: function() {
    console.log(this);          // { outer: f }
    var innerFunc1 = () => {    // { outer: f }
      console.log(this);
    };
    innerFunc1();
  }
};
obj.outer();

 

이외에 명시적으로 this 를 바인딩하는 방법이 있다. call, apply, bind 등의 메서드를 활용하면 된다.
아래에서 자세히 살펴보자.

 

CALL 메서드

var func = function (a, b, c) {
  console.log(this, a, b, c);
}

func(1, 2, 3);                 // Window{ ... } 1 2 3 
func.call({ x: 1 }, 4, 5, 6);  // { x: 1 } 4 5 6

call 메서드를 이용하면 임의의 객체를 this 로 지정할 수 있다.

 

APPLY 메서드

var func = function(a, b, c) {
  console.log(this, a, b, c);
};
func.apply({ x: 1 }, [4, 5, 6]); 	// { x: 1 } 4 5 6

var obj = {
  a: 1,
  method: function(x, y) {
    console.log(this.a, x, y);
  }
};
obj.method.apply({ a: 4 }, [5, 6]); // 4 5 6

 

apply 메서드는 call 메서드와 기능적으로 완전 동일하다.
call 메서드는 첫번째 인자를 제외한 나머지 모든 인자들을 호출할 함수의 매개변수로 지정하지만, apply 메서드는 두번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다는 점에서 차이가 있을 뿐이다.

 

BIND 메서드

var func = function(a, b, c, d) {
  console.log(this, a, b, c, d);
};
func(1, 2, 3, 4);                           // Window{ ... }

var bindFunc1 = func.bind({ x: 1 });
bindFunc1(5, 6, 7, 8);                      // { x: 1} 5 6 7 8 

var bindFunc2 = func.bind({ x: 1 }, 4, 5);
bindFunc2(6, 7);                            // { x: 1 } 4 5 6 7 
bindFunc2(8, 9);                            // { x: 1 } 4 5 8 9 

위와 같이 자주 사용된다는데.. 아직은 잘 모르겠다... call, apply, bind 메서드의 사용방법은 쉬우면서도 어려운 것 같다ㅠㅠ

 

화살표 함수의 예외사항 

var obj = {
  outer: function() {
    console.log(this);        // { outer: f }
    var innerFunc = () => {
      console.log(this);      // { outer: f }
    };
    innerFunc();
  }
};
obj.outer();

ES6 에 새롭게 도입된 화살표 함수는 실행 컨텍스트 생성할 때 this 를 바인딩하는 과정이 제외되었다.
다시 말해서 이 함수 내부에는 this 가 아예 없으며, 접근하고자 하면 스코프체인상 가장 가까운 this 에 접근하게 되는 것이다.

다음은 콜백 함수에 대해 집중적으로 공부해보자.

 

 

* 출처 : '코어 자바스크립트 - 핵심 개념과 동작 원리로 이해하는 자바스크립트 프로그래밍', 정재남 지음

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기