요약
javascript에서 함수의 this
키워드는 다른 언어들과 비교하여 조금 다르게 동작한다. 또한 strict mode와 non-strict mode 사이에서도 조금 다르다.
대부분의 경우, this
의 값은 함수를 호출하는 방법에 의해 결정된다. 실행하는 동안의 할당에 의해 설정될수 없고, 함수가 호출될 때 마다 다를 수 있다. ES5 는 함수의 this
값이 함수가 어떻게 호출되었는지 개의치 않고 설정할 수 있는 bind
메소드를 소개했다.
구문
this
전역 컨텍스트
전역 실행 컨텍스트(어떤 함수의 외부)에서는, this
는 strict mode이거나 아니여도 전역 객체를 나타낸다.
console.log(this.document === document); // true // 웹 브라우저에서, window 객체는 전역 객체이다 : console.log(this === window); // true this.a = 37; console.log(window.a); // 37
함수 컨텍스트
함수 내부에서, this
는 함수를 호출한 방법에 의해 좌우된다.
간단한 호출
function f1(){ return this; } f1() === window; // global object
이 경우, this
의 값은 호출에 의해 설정되지 않는다. 이 코드는 strict mode가 아니기 때문에, this의 값은 항상 전역 객체에서 기본이 되는 객체여야 한다.
function f2(){
"use strict"; // see strict mode
return this;
}
f2() === undefined;
strict mode에서는, this
의 값은 실행 컨텍스트 들어갈 때 할당되어 유지된다. 만약 정의 되지 않았다면, undefined
가 유지된다. 게다가 null
또는 42
또는 "I am not this" 등
어떤 값이던 설정할 수 있다.
Note: 두번째 예제에서, this
는 undefined
일것이다, 왜냐하면 f2가 어떠한 기본(예. f2()
)도 제공하지 않고 호출했다. 이 기능은 strict mode를 지원하기 위해 시작했을 때 일부 브라우저에서 구현되지 않았다. 결과적으로, window
객체를 잘못 반환했다.
객체의 메소드로서
함수가 한 객체의 메소드로 호출되었을 때, this
는 메소드가 호출된 객체가 할당됩니다.
다음 예제에서, o.f()
가 실행될 때, 함수 안쪽의 this
는 o
객체가 연결된다.
var o = { prop: 37, f: function() { return this.prop; } }; console.log(o.f()); // logs 37
this
동작이 함수가 정의된 방법이나 위치에 전혀 영향을 받지 않는 것에 유의해라. 이전 예제에서, o
의 정의 중 f
함수를 구성원으로 내부에 정의 하였다. 그러나, 간단하게 함수를 먼저 정의하고 나중에 o.f
를 추가할 수 있다. 이렇게 하면 동일한 동작 결과 이다 :
var o = {prop: 37}; function independent() { return this.prop; } o.f = independent; console.log(o.f()); // logs 37
이는 함수가 o
의 멤버 f
로 부터 호출 된 것만이 중요하다는 것을 보여준다.
마찬가지로, 이 this
바인딩은 가장 즉각으로 멤버 대상에 영향을 받는다. 다음 예제에서, 함수를 실행할 때, 객체 o.b
의 메소드 g
로서 호출한다. 이것이 실행되는 동안, 함수 내부의 this
는 o.b
를 나타낸다. 객체는 그 자신이 o
의 멤버 중 하나라는 사실은 중요하지 않다. 가장 즉각적인 참조가 중요한 것이다.
o.b = {g: independent, prop: 42}; console.log(o.b.g()); // logs 42
객체의 prototype 체인에서 this
같은 개념으로 객체의 prototype 체인 어딘가에 정의된 메소드도 마찬가지이다. 메소드가 한 객체의 prototype 체인에 있다면, 마치 메소드는 객체에 있는 것 처럼 this는 메소드가 호출된 객체를 나타낸다.
var o = {f:function(){ return this.a + this.b; }}; var p = Object.create(o); p.a = 1; p.b = 4; console.log(p.f()); // 5
이 예제에서, f
속성을 가지고 있지 않은 변수 p
가 할당된 객체는, prototype으로 부터 상속받는다. 그러나 그것은 결국 o
에서 f
이름을 가진 멤버를 찾는 되는 문제가 되지 않는다 ; p.f
를 찾아 참조하기 시작하므로, 함수 내부의 this
는 p
처럼 나타나는 객체 값을 취한다. 즉, f
는 p
의 메소드로서 호출된 이후로, this
는 p
를 나타낸다. 이것은 Javascript의 prototype 상속의 흥미로운 기능이다.
getter와 setter에서의 this
다시, 함수가 getter와 setter에서 호출될 때도 같은 개념이다. getter와 setter로 사용되는 함수의 this
는 set
또는 get
되는 속성 객체로 연결된다.
function modulus(){
return Math.sqrt(this.re * this.re + this.im * this.im);
}
var o = {
re: 1,
im: -1,
get phase(){
return Math.atan2(this.im, this.re);
}
};
Object.defineProperty(o, 'modulus', {
get: modulus, enumerable:true, configurable:true});
console.log(o.phase, o.modulus); // logs -0.78 1.4142
생성자로서
함수가 생성자로서 사용될 때(new
키워드와 함께), this
는 새롭게 생성된 객체에 연결된다.
Note: 생성자의 기본은 this
에 의해 참조되는 객체를 반환하지만, 가끔 다른 객체를 대신 반환할 수도 있다.(반환 값이 객체가 아닌 경우, this
객체를 반환한다.)
/*
* 생성자는 이와 같이 작동한다:
*
* function MyConstructor(){
* // 실제 함수 본문 코드가 에기에 표시된다.
* // this에 할당하여 원하는 대로 속성을 만들 수 있다. 예를 들어,
* this.fum = "nom";
* // 등등...
*
* // 함수가 객체를 반환하는 return 문이 있다면,
* // 그 객체는 |new| 표현식의 결과가 될 것이다.
* // 그렇지 않다면,
* // 표현식의 결과는 현재 |this|에 연결된 객체이다.
* // (즉, 일반적 경우 대부분 이런 것을 볼 수 있다.).
* }
*/
function C(){
this.a = 37;
}
var o = new C();
console.log(o.a); // logs 37
function C2(){
this.a = 37;
return {a:38};
}
o = new C2();
console.log(o.a); // logs 38
마지막 예제에서 (C2
), 이 객체는 생성하는 동안 return
되었기 때문에, this
에 연결된 새로운 객체는 그냥 버려진다. (이것은 본질적으로 "this.a = 37;
"가 죽은 코드를 만든다. 그것은 실행되지 때문에 명확히는 죽은 것은 아니지만, 어떤 외부 영향을 받지 않는다.)
call
그리고 apply
함수의 본문(내부)에서 this
키워드를 사용하는 경우, Function.prototype
으로 부터 상속되는 함수의 Function.call()
과 Function.apply()
를 사용하여 호출하면 특정한 객체와 연결된다.
function add(c, d){
return this.a + this.b + c + d;
}
var o = {a:1, b:3};
// 첫번째 매개 변수는 'this'로 사용할 객체이고,
// 다음 매개 변수들은 호출되는 함수에 인수로 전달된다.
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
// 첫번째 매개 변수는 'this'로 사용할 객체이고,
// 두번째는 호출되는 함수에 인수로 사용되는 멤버들의 배열이다.
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
call
과 apply
에서 주의할 것은, this
를 나타내는 값이 객체가 아니라면, 내부 ToObject
를 이용하여 객체로 변환 될 것이다. 그래서 '7
' 또는 'foo
' 같은 기본 형태의 값이 전달되면, 연관된 생성자를 사용하여 객체로 변환할 것할 것이다. 기본 숫자 7
은 new Number(7)
, 문자 'foo'
는 new String('foo')
를 사용하여 객체로 변환된다. 예를 들어,
function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); // [object Number]
bind
메소드
ECMAScript 5는 Function.prototype.bind
를 도입했다.
f.bind(객체)를 호출하면 같은 본문(코드)과 asf 생명주기로 새로운 함수를 생성한다.
원래의 함수에서 발생하는 this
지만, 함수가 어떻게 사용되고 있는지 개의치 않고, 새로운 함수에서 this
는 bind
의 첫번째 인수로 영구적으로 연결된다,
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty
DOM 이벤트 핸들러로서
함수가 이벤트 핸들러로 사용될 때는, this는 이벤트가 발생한 엘리먼트로 설정된다.
(몇몇 브라우저들은 리스너를 addEventListener
이외의 방법으로 동적으로 추가하기 위하여 이 관습을 따르지 않는다.)
// 리스너로서 호출될 때, 관련된 엘리먼트를 파랗게 만든다.
function bluify(e){
// 항상 true
console.log(this === e.currentTarget);
// currentTarget 과 target 이 같은 객체일 때 true
console.log(this === e.target);
this.style.backgroundColor = '#A5D9F3';
}
// document의 모든 엘리먼트의 목록을 얻는다.
var elements = document.getElementsByTagName('*');
// 클릭 리스너로 bluify를 추가하여서 엘리먼트를 클릭하면, 그 엘리먼트는 파랗게 된다.
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}
in–line 이벤트 핸들러에서
코드가 in-line 핸들러에서 호출 될 때, this는 리스너가 위치한
DOM 엘리먼트가 할당된다:
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
위의 alert는 button이 보여진다.
참고로 오직 외부 코드에서 this
를 할당 할 수 있다:
<button onclick="alert((function(){return this}()));">
Show inner this
</button>
이 경우에서는, 함수 내부의 this
할당 되지 않았으므로 전역/window
객체가 반환된다. (즉, this는 호출에 의해 할당되지 않고 non-strict mode의 기본 객체).
설계 명세
Specification | Status | Comment |
---|---|---|
ECMAScript 1st Edition. | Standard | 초기 정의. JavaScript 1.0에서 구현 |
ECMAScript 5.1 (ECMA-262) The definition of 'The this keyword' in that specification. |
Standard | |
ECMAScript 6 (ECMA-262) The definition of 'The this keyword' in that specification. |
Release Candidate |
브라우저 호환성
Feature | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Basic support | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) |
Feature | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
Basic support | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) |
참조
- Strict mode
- All this, 다른 컨텍스트의
this
에 대한 글