자바스크립트의 this가 각 컨텍스트(context)마다 어떠한 방식으로 동작하는지 MDN 문서를 참고하여 정리해보았다.


this 키워드

this는 자바스크립트에서 함수의 현재 실행 문맥이다. 따라서 this가 쓰여지고 호출되는 방식에 따라 참조하는 객체가 달라진다.

alert('Hello world');  // 함수 실행
console.log('Hello world');  // 메소드 실행
new myConstructor();  // 생성자 실행
alert.call(undefined, 'Hello world');  // 간접 실행

위와 같은 실행 타입의 차이와 실행모드(일반, 엄격)는 this의 실행 문맥에 영향을 미친다.


1. 전역 컨텍스트

this는 기본적으로 window, 즉, 브라우저의 전역객체이다.


console.log(this.document == document); // true

console.log(this === window); // true

this.a = 30;
console.log(window.a); // 30


2. 함수 컨텍스트

// 함수 내부에서 this는 함수를 호출한 방법에 의해 좌우된다.
// - 아래 예시에서는 호출에 의해 설정되지 않는다. 
// -strict 모드가 아니므로 항상 전역 객체에서 기본이 되는 객체이다.
function f1(){
    return this;
}

console.log(f1() === window); // true


// strict mode에서 this는 실행 컨텍스트에서 할당되고 유지된다.
function f2(){
    "use strict"
    return this;
}
// f2()내의 this는 undefined인데, f2가 객체의 메소드나 프로퍼티가 아니라 직접 호출되었기 때문이다. 
// 결과적으로 window 객체로 잘못 반환한 것이다.
console.log(f2() === undefined);  // true


3. 객체의 메소드로서 this

(1) this는 실행되는 시점의 문맥을 따른다.

var myCar2 = {
    
    maxSpeed: 80,
    driver: "julia",
    drive: function(speed, time){
        console.log(speed * time);
    }, 
    test: function(){
        console.log(this);
    }
};

var myCar3 = {
    
    maxSpeed: 90,
    driver: "katie",
    drive: function(speed, time){
        console.log(speed * time);
    }, 
    test: function(){
        console.log(this);
        // 다음과 같이 써도 되지만 this를 쓰면 리팩토링을 줄여준다. 
        console.log(myCar3);
    }
};

console.log(this);
myCar2.test();  // myCar2 객체 출력
myCar3.test();  // myCar3 객체 출력


(2) this는 함수가 정의된 방법이나 위치에 구애받지 않는다. 다음과 같이 변수에 함수로 할당할 수도 있다.

var myVar1 = {
    prop:30,
    f: function(){
    return this.prop;
    }
};

console.log(myVar1.f());  // 30


(3) (2)의 예제에서 f 함수를 밖에 정의해도 동일하게 동작한다.

var myVar2 = {prop: 30};

function independent(){
    return this.prop;
}

myVar2.f = independent;

console.log(myVar2.f()); // 30


(4) this는 멤버 대상에 영향을 받는다.

myVar2.b = {f3: independent, prop: 51};

/* 아래 myVar3.b의 메소드 f3이 실행되는 동안 
함수 내부의 this는 myVar3.b를 나타낸다. 가장 즉각적인 참조역할을 수행하는 것이다. */
console.log(myVar2.b.f3()); // 51


4. 객체의 prototype 체인에서 this

var myVar4 = {
    f:function(){return this.a + this.b; 
    }
};
// 메소드가 한 객체의 prototype 체인에 있으면 메서드는 객체에 있는 것처럼 this는 메소드가 호출된 객체를 나타낸다.
// myVar5는 f 속성을 가지고 있지 않지만 prototype으로부터 상속받았다.

var myVar5 = Object.create(myVar4);
myVar5.a = 1;
myVar5.b = 2;

// myVar5는 f 멤버를 가지게 되고 함수 내부의 this는 myVar5를 나타낸다. 
// 이는 자바스크립트 prototype 상속의 특징이다. 
console.log(myVar5.f()); // 3


5. 생성자 함수에서 this

// - 생성자의 기본은 this에 의해 참조되는 객체를 반환하지만 가끔 다른 객체를 반환할 수 있다.
// - 반환값이 객체가 아니면 this객체를 반환한다.

/* 생성자 함수 작동원리
function myConstructor(){
    - 실제 함수로직코드를 작성하는 부분이다.
    - this에 할당해 속성을 만든다.
      ex_ this.myVar = 1;
}

생성자의 실행은 표현식 앞에 new라는 키워드가 붙었을 때
함수 객체로 계산되어 수행된다.
ex_ var myVarEx = new myConstructor();
*/

// (1) 함수내에 return문이 없으면 객체는 'new 표현식'의 결과처럼 작동한다.
function A1(){
    this.p1 = 37;
}

var a = new A1();
console.log(a.p1);  // 37

// (2) return값이 있으면 결과는 this에 연결된 객체로 반환된다.
function A2(){
    this.p2 = 60;
    return {p2:63};
}

var b = new A2();
// 이 때 this.p2 = 60; 객체는 생성하는 동안 return되었으므로 this에 연결된 새로운 객체는 그냥 버려진다.
console.log(b.p2);  // 63


6. f.call()f.apply()에서 this

// - 함수 내부에서 this를 사용할 때 특정 객체와 연결하여 사용할 수 있다.

function add(c, d){
    return this.a + this.b + c + d;
}

var myVar6 = {a:1, b:3};

// this로 사용할 객체와 매개변수 인자값을 전달하였다.
console.log(add.call(myVar6, 4, 5));  // 1+3+4+5=13  

// this로 사용할 객체와 함수의 인수로 사용되는 배열을 전달하였다.
console.log(add.apply(myVar6, [7, 8]));  // 1+3+7+8=19

// 이 때 this로 연결된 값이 객체가 아니면 내부 ToObject를 사용하여 객체로 변환될 것이다.
// 기본 값이 전달되면 다음과 같이 객체로 생성된 후 전달된다.
function foo(){
    console.log(Object.prototype.toString.call(this));
}

foo.call(7); // [object Number]


7. bind 메소드에서의 this

/* - ECMAScript5에서 Function.prototype.bind 
메소드를 도입하여 f.bind(객체)를 호출하면 
다음과 같은 asf 생명주기로 새로운 함수를 생성한다.
보통 this는 메소드가 정의되어 있는 객체라고 생각하는데

객체 밖에 있는 메소드를 사용할 경우 
bind 함수로 메소드를 객체에 포함시킬 수 있다. */
function f(){
    return this.a;
}

var g = f.bind({a:"julia"});
console.log(g());  // julia

var o = {a:29, f:f, g:g};
console.log(o.f(), o.g()); // 29, "julia"


8. DOM 핸들러로서의 함수에서 this

/* - 함수가 이벤트 핸들러로 사용될 때 
this는 이벤트가 발생한 엘리먼트로 설정된다.*/
function turnBlue(e){
    // 항상 true 반환
    console.log(this === e.currentTarget);
    
    // currentTarget과 target이 같은 객체이면 true
    console.log(this === e.target);
    this.style.backgroundColor = "#blue";
};

// document의 모든 엘리먼트 목록 반환
var elements = document.getElementsByTagName("*");

/* 클릭 리스너로 위 함수를 추가해준후 
엘리먼트를 클릭하면 파랗게 표시된다.
몇몇 브라우저는 이외의 방법을 사용하기도 하므로
이 관습을 따르지 않을 수 있다.
*/
for(var i=0; i<elements.length; i++){
    elements[i].addEventListener('click', turnBlue, false);
}


9. in-line 이벤트 헨들러로서의 함수에서 this

<!--코드가 in-line 핸들러에서 호출될 때 
this는 리스너가 위치한 DOM 엘리먼트로 할당된다.
-->

<!--(1) 오직 외부 코드에서 this를 할당 가능-->
<button onclick="alert(this.tagName.toLowerCase());">Show this  <!--클릭시 button 출력-->
</button>

<!--(2) this는 전역/window 객체가 반환
- 함수 내부의 this가 할당되지 않았기 때문이다.
- 즉, 아래 코드의 this는 호출에 의해 할당되지 않고 non-strict 모드의 기본객체이다.-->
<button onclick="alert(function(){return this}());">Show inner this  <!--클릭시 [object Window] 출력-->
</button>



참고자료

1) MDN 문서 - this

2) 번역 및 추가예제


Julia Hwang

디발자를 꿈꾸는 웹개발자의 블로그입니다.