this

this


1. this

  • this는 실행 컨텍스트가 생성 될 때 함께 결정됌.
  • 따라서 this는 함수를 호출할 때 결정되는것이다.

(1) 전역 공간에서의 this

  • 전역 공간에서 this는 전역 객체를 가르킨다.
  • 따라서 브라우저 환경의 thiswindow를 가르킴
  • 따라서 Node.js 환경의 thisglobal을 가르킴
var a = 1;
console.log(a); // 1
console.log(window); // 1
console.log(this.a); // 1
  • 이 코드에서는 전역 공간에서 선언한 변수에 1을 할당했는데 window , this가 모두 1이 출력됌 ⇒ this는 전역객체이므로 window 객체가 나와야한다 근데 1이 출력됌
  • 여기서 중요한 것은 전역변수를 선언하면 JavaScript 엔진은 전역 객체의 프로퍼티로 할당된다
  • 이유는 변수 a에 전역객체에서 전역 스코프 L.E , 선언한 프로퍼티 a를 발견해서 그 값을 반환했기 때문에 1이 출력된것이다

따라서 window 프로퍼티에 할당해도 var로 선언한 결과가 똑같다

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

(2) 메서드로서 호출할 때 메서드 내부에서의 this

  1. 함수 호출: 독립 적인 기능 수행
  2. 메서드 호출: 자신을 호출한 대상 객체에 관한 동작 수행
var func = function (x) {
  console.log(this, x);
};
// (1) 함수 호출
 
func(1);
 
//Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, …}
var obj = {
  method: func,
};
 
// (2) 메서드 호출 (점 표기법)
 
obj.method(2);
// {method: ƒ}method: ƒ (x)[[Prototype]]: Object 2
 
// (3) 메서드 호출 (대괄호 표기법)
 
var obj2 = {
  method: function (x) {
    console.log(this, x);
  },
};
 
obj2["method"](2);
  • 쉽게 구분 하는 방법은 함수 앞에 점(.)이 있냐 없냐 대괄호가 있냐 없냐의 판단으로 쉽게 구분 가능하다

따라서 점 표기법에선 마지막 점 앞에 명시된 객체가 this라는 것이다

(3) 함수 호출로서 호출할 때 함수 내부의 this

var obj1 = {
  outer: function () {
    console.log(this); // (1) obj1
    var innerFunc = function () {
      console.log(this); // (2) window // (3) obj2
    };
    innerFunc();
    var obj2 = {
      innerMethod: innerFunc,
    };
    obj2.innerMethod();
  },
};
obj1.outer();

this를 이해 하기 위한 코드 실행 과정

  • 객체를 생성하고 outer() 프로퍼티를 obj1에 할당
  • obj.outer() 호출
  • obj.outer()의 실행 컨텍스트 생성 및 innerFuncobj2를 Hoisting
  • console.log(this)에서 해당 this를 바인딩한다
  • 점 표기법으로 메서드를 호출했으니 (1)은 점 앞의 객체 obj1를 바인딩
  • innerFunc에 익명함수를 할당하고 호출
  • innerFunc의 실행 컨텍스트 생성 및 Hoistng
  • console.log(this)에서 해당 this를 바인딩한다
  • this 호출엔 점 표기법으로 호출되지 않았으니 전역객체 window가 바인딩된다
  • obj2innerMethod 프로퍼티에 innerFunc를 할당하고 다시 호출
  • 여기선 또 호출 시 innerMethod앞에 obj2의 점표기로 호출 했으니 obj2가 바인딩된다

주변 환경은 중요하지 않고 해당 함수 호출 부분에 따라서 this는 점 표기 인지 대괄호 표기를 확인해야 this가 어떤 걸 바인딩하는지 알 수 있다

(1) 메서드 내부에서 this 우회 방법

  • this에 변수를 활용하는 방법이 있다

(2) this 바인딩을 하지 않는 함수

  • ES6에서 화살표 함수는 this 바인딩 과정 자체가 빠져서 상위 스코프의 this를 활용 할 수 있다. 따라서 ES6부턴 우회법이 불필요하다

(4) 콜백 함수 호출에서의 this

  • 콜백 함수도 함수라 기본적으로 this가 전역객체를 참조하지만, 제어권을 받은 함수에서 콜백함수에 별도로 this가 될 대상을 지정한 경우는 그 대상을 참조하게 된다.
setTimeout(function () {
  console.log(this), 300;
}); // (1) : window
 
[1, 2, 3, 4, 5].forEach(function (x) {
  console.log(this, x); // (2) : window
});
 
document.body.innerHTML += '<button id ="a">클릭</button>';
document.body.querySelector("#a").addEventListener("click", function (e) {
  console.log(this, e); // (3) : document.body.querySelector('#a')
});
  • 다만 콜백 함수에선 this가 무조건적이진 않다

(5) 생성자 함수 내부에서의 this

  • 생성자 함수에서 호출된 경우의 this는 새로 만들 구체적인 인스턴스 자신이다
var Cat = function (name, age) {
  this.bark = "야옹";
  this.name = name;
  this.age = age;
};
 
var choco = new Cat("초코", 7); // (1): Cat { bark: '야옹', name: '초코', age: 7 }
var nabi = new Cat("나비", 5); //  (2): Cat { bark: '야옹', name: '나비', age: 5 }
console.log(choco, nabi);

2. 명시적 this 바인딩

(1) call

  • 메서드 호출 주체인 함수를 즉시 실행하는 명령
  • call 메서드의 첫번째 인자를 this 바인딩하고 나머지 인자들을 호출할 함수의 매개변수로 할당
  • call 메서드는 임의의 객체를 this로 지정 가능함
var obj = {
    a:1,
    method:function(x,y){
        console.log(this.a,x,y);
    }
};
 
obj.method(2,3);
obj.method.call({a:4},5,6);
 
// 1 2 3
// 4 5 6
 
[Done] exited with code=0 in 0.051 seconds
 

call 메서드 활용

  • 유사배열객체에 배열 메서드 적용할때
  • argument ,NodeList에 배열 메서드 적용할때
  • 문자열에 배열 메서드 적용할때
var obj1 = {
  0: "a",
  1: "b",
  2: "c",
  length: 3,
};
 
Array.prototype.push.call(obj1, "d");
console.log(obj1);
 
var arr1 = Array.prototype.slice.call(obj1);
console.log(arr1);

(2) apply

  • 기능적으론 call과 동일함
  • 차이점은 call 메서드는 첫번째 인자를 제외한 모든 인자들을 매개 변수로 지정하지만 apply 메서드는 두번째 인자를 배열로 받아서 처리
var func = function(a,b,c){
    console.log(this,a,b,c);
}
 
func.apply({x:1},[4,5,6]);
 
var obj = {
    a:1,
    method:function(x,y){
        console.log(this.a,x,y);
    }
};
 
obj.method.call({a:4},5,6);
 
// { x: 1 } 4 5 6
// 4 5 6
 
[Done] exited with code=0 in 0.052 seconds

ES6 대안

  • Array.from
var obj = {
    0:'a',
    1:'b',
    2:'c',
    length:3,
}
 
var arr =Array.from(obj);
console.log(arr);
 
// [ 'a', 'b', 'c' ]
 
[Done] exited with code=0 in 0.045 seconds

apply 메서드 활용

  • 최대/최소값 구하는 코드
var numbers = [10, 20, 3, 16, 45];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
console.log(max, min);

ES6 대안

  • Spread Operator
var numbers = [10, 20, 3, 16, 45];
var max = Math.max(...numbers);
var min = Math.min(...numbers);
console.log(max, min);

(3) bind

  • call과 비슷하지만 즉시 호출하지 않고 넘겨 받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 함
  • bind의 핵심은 this를 미리 적용 , 부분 적용 함수를 구현
var func = function(a,b,c,d){
    console.log(this,a,b,c,d);
}
 
func(1,2,3,4);
 
var bindFunc1 = func.bind({x:1});
bindFunc1(5,6,7,8);
 
// Window{...} 1 2 3 4
// { x: 1 } 5 6 7 8
 
[Done] exited with code=0 in 0.061 seconds

ES6 대안 : 화살표 함수

  • 화살표 함수는 this를 바인딩하는 과정이 제외됐음
  • 내부의 this는 없고 접근하면 가장 가까운 스코프체인의 this에 접근
  • 따로 this 우회할 필요도 없고 call/apply/bind를 적용 할 필요 없음

(4) 정리

  • 전역 공간에서의 this : 전역객체( window , global..)
  • 함수 메서드에서의 this : 메서드 호출 주체 참조
  • 함수 호출에서의 this : 함수 호출 주체 참조 (기본적으론 전역 객체 참조)
  • 콜백 함수에서의 this : 콜백 함수의 제어권을 넘겨받은 함수 주체 참조 (기본적으론 전역 객체 참조)
  • 생성자 함수에서의 this : 인스턴스 대상 참조

화살표 함수를 쓰면 this 명시적 바인딩을 적용 할 필요가 없다