this

this가 정해지는 시기

실행컨텍스트가 활성화 될때 thisBinding을 한다.

실행 컨택스트는 함수가 실행될 때 생성되므로 this는 함수가 실행 될 때 결정된다(동적 binding).

그래서 함수를 어떤 식으로 호출 했느냐에 따라서 this는 얼마든지 달라질 수 있다.

전역 공간에서 호출 했을 때

전역 객체를 가리킨다. 브라우저에서는 window, node.js 에서는 global. host 객체라고도 한다.

함수로 호출 했을 때

함수를 호출했을 때도 전역 객체를 가리킨다.

전역 객체에서 함수를 호출했기 때문에 호출한 대상이 전역 객체가 된다.

그런데 내부 함수에서도 this를 전역객체를 가리킨다. 이 부분을 자바스크립트의 오류라고도 이야기한다고 한다.

그래서 ES6에서 나온 게 arrow function이다.

arrow funtion은 thisBinding을 하지 않고 바로 위 컨텍스트에 있는 this를 쓴다.

(하지만 ES5에서는 함수로 호출했을 때의 this는 무조건 전역 객체를 가리킨다.)

메서드로 호출 했을 때

객체내의 함수, 객체의 메서드로 호출 했을 때는 객체가 this로 바인딩 된다.

하지만 메서드 안에서 내부 함수를 호출 했을 때(c();)는 전역 객체를 가리킨다. (함수에서 this는 무조건 전역객체이기 때문 )

var a = {
	b : function(){
		console.log(this);
		function c (){
			console.log(this)
		}
		c(); // window 
	}
}
a.b(); // a()

var a = {
	b: {
		c: function(){
			console.log(this);
		}
	}
} 
a.b.c(); // a.b()

객체의 메서드는 인스턴스(객체)와 관련된 동작이다.

메서드는 원래는 함수이지만 어떠한 객체(인스턴스)와 관련된 동작을 하게 되면 메서드라고 칭한다.

메서드 내부 함수에서 this가 전역 객체를 가리키는 문제 해결점

  1. call, apply 같은 this 바인딩 명령으로 this를 명시시켜 준다.
         b : function(){
             function c = () =>{
                 console.log(this.a); // b에서의 this를 따라간다. 
             }
             c.call(this);
         }
    
  2. this 우회법 : this를 우회해서 표현한다.
    • 스코프 체인을 사용해서 다른 변수로 넣어줄 수 있다
        var a = 10;
        var obj = {
            a : 20,
            b : function(){
                var self = this; // self 말고도 _this, that 등으로도 사용한다 
                console.log(this.a); // obj를 가리키므로 20
      
                function c (){
                    console.log(this.a) // window를 가리키므로 10
                }
                c(); 
      
                function d(){
                    console.log(self.a); //obj를 가리키게 되서 20
                }
                d();
            }
        }
        obj.b(); 
      
    • b()에서 thisself에 담아 줬기 때문에 d()에서 self를 찾았을 때 selfb()에서의 this를 담고 있게 된다.
  3. arrow function을 사용한다 (ES6)
    • this를 바인딩하지 않기 때문에 스코프 체인 상에서 상위 this에 바로 접근 할 수 있다.
        b : function(){
            function c = () =>{
                console.log(this.a); // b에서의 this를 따라간다. 
            }
            c();
        }
      

callback으로 호출 했을 때

기본적으로는 함수 내부에서와 동일하고(this === 전역객체) 어떻게 호출하느냐에 따라 다르다.

var callback = function(){
	console.dir(this);
};
var obj = {
	a: 1;
	b: function(cb){
		cb();
	}
}
obj.b(callback);

메소드로 b()를 실행하면서 callback을 함수로 넘겨줬다.

obj의 매개변수 b에서 callback이 인자로 넘어가서 cb();로 호출되었다.

이때 callback은 매개변수가 아닌 함수의 형태(cb();)로 실행되었기 때문에 this는 window가 된다.

var callback = function(){
	console.dir(this);
};
var obj = {
	a: 1;
	b: function(cb){
		cb.call(this);
	}
}
obj.b(callback);

cb()를 그냥 호출하지 않고 call()를 이용해서 this를 넘겨주면 b() 컨택스트의 thisobj가 this가 되고 callback에서 obj를 this로 받는 다.

이렇게 callback함수에서의 this는 callback함수 자체가 결정하는 것이 아니고 callback 함수를 넘겨받는 대상(위 코드에서는 b())이 어떤 형식으로 처리를 하느냐에 따라서 결정된다.

기본적으로 함수라 전역객체이지만 this를 지정해서 처리해주면 그 this를 따른다.

이벤트에서의 callback()

document.body.innerHTML += '<div id="a">클릭하세요</div>';

document.getElementById('a').addEventListener(
	'click',
	function(){
		console.dir(this); // div#a
	}
);

addEventListener()에서이벤트 핸들러로 callback함수를 넘기면 this는 이벤트가 발생한 그 타겟대상 엘리멘트로 호출된다.

이건 addEventListener()가 callback함수를 처리할 때 this를 그렇게 지정하도록 정의가 되어 있기 때문이다.

이벤트 실행시에도 this를 지정해주고 싶으면 아래 코드처럼 콜백함수에 this를 bind 시켜주면 된다.

document.getElementById('a').addEventListener(
	'click',
	function(){
		console.dir(this); // div#a
	}.bind(obj)
);

callback으로 호출 했을 때의 this 정리

생성자 함수로 호출 했을 때

생성자 함수 new 연산자를 사용 했을 때의 this는 무엇을 가리킬까?

new 연산자를 썼다는 말은 생성자 함수의 내용을 바탕으로 인스턴트 객체를 만드는 명령이다.

새로 만들 인스턴트 객체 그 자체가 this가 된다.

function Person(n, a){
	this.name = n;
	this.age = a;
}
var laura = Person('로라', 20);
console.log(window.name, window.age); // 로라 20 

이렇게 함수로 Person()을 실행하면 this는 전역객체 window가 들어가게 되므로 window의 프로퍼티 nameage로 값이 들어간다.

function Person(n, a){
	this.name = n;
	this.age = a;
}
var laura = new Person('로라', 20);
console.log(laura); // Person(){name: '로라', age:20}

new 연산자를 사용해서 Person을 생성자 함수로 호출하면 laura라는 변수에 Person의 인스턴트 객체가 생성되어 들어가는 데 이때 this인스턴트 자신이므로 this는 Person()이 되어 Person()nameage에 각각 ‘로라’, 20이라는 값이 할당되어 생성된다.

Reference

Discussion and feedback