Javascript에서 this란 무엇이고, 어떤 역할을 하는지, 상황에 따라 어떻게 this를 해석해야 하는지 알아봅니다.
this는 현재 실행 중인 코드가 속한 개체, 클래스 또는 다른 엔티티를 참조하기 위해 일부 컴퓨터 프로그래밍 언어에서 사용되는 키워드입니다.
this에 의해 참조되는 엔터티는 실행 컨텍스트(예를 들어 메소드가 호출되는 객체)에 따라 달라집니다. (by wiki)
위 설명에 의하면 실행 컨텍스트에 따라 this 값이 달라진다고 합니다.
우선 맨 처음 시작 점인 전역 컨텍스트에서 this를 알아볼까요?
브라우저에서 F12를 눌러 콘솔 창을 열어봅시다.
거기서 this를 호출하면 무엇이 나올까요?
window란 객체가 출력됩니다. (nodejs 콘솔 창에서는 global이 나옵니다.)
브라우저에서의 this 값
nodejs에서의 this 값
현재 브라우저에 열린 콘솔 창은 전역 실행 문맥입니다.
전역 실행 문맥(global execution context)에서 this는 전역 객체를 참조합니다.
즉, 브라우저의 전역객체는 window이고 nodejs의 전역객체는 global이란 거죠~
메서드 내부 코드에서 사용된 this는 해당 메서드를 호출한 객체로 바인딩됩니다.
function getThis() {
return this;
}
내부 함수에서 this를 호출하면 전역 객체인 window가 호출됩니다.
예제 1에서 getThis 함수는 전역에서 정의되었습니다.
전역에서 정의되었다는 것은 window 객체에 정의 되었다는 것입니다.
그러므로 getThis() 함수 호출은 window.getThis()와 동일한 호출입니다.
window 객체가 getThis를 호출했기 때문에 getThis 함수 내부에서 this는 window 객체를 가르키게 됩니다.
var John = {
name: 'John',
getName : function () {
return this.name;
},
getNameFunc : function () {
return this.getName;
}
};
예제 2에서 John.getName() 이라고 실행할 경우 getName 내부의 this는 어떻게 될까요?
getName을 호출하는 John 객체가 됩니다.
만약 전역인 window 객체에서 getName() 호출한다면 this는 어떻게 될까요?
getName 내부의 this는 window 객체가 됩니다.
var John = {
name: 'John',
getName : function () {
addGender = function () {
return this.name + ':남자'
}
return addGender();
}
};
John.getName()을 통해 ‘John:남자’라는 값을 기대했지만, ‘:남자’가 나오게 된다. addGender 함수 내부에서 this가 John이 아닌 Window를 의미하기 때문이다.
var John = {
name: 'John',
getName : function () {
var that = this;
addGender = function () {
return that.name + ':남자'
}
return addGender();
}
};
var John = {
name: 'John',
getName : function () {
addGender = function () {
return this.name + ':남자'
};
addGender = addGender.bind(this);
return addGender();
}
};
var John = {
name: 'John',
getName : function () {
addGender = function () {
return this.name + ':남자'
};
return addGender.call(this)
}
};
var John = {
name: 'John',
getName : function () {
addGender = function () {
return this.name + ':남자'
};
return addGender.apply(this)
}
};
var foo = {};
Person.apply(foo, [‘foo’, ‘30’, ‘man’]); console.dir(foo); Person.call(foo, ‘foo’, ‘40’, ‘man2’); console.dir(foo);
- [부록 3. this 바인딩 활용 예제](#부록-3-this-바인딩-활용-예제)
<br/>
#### #해결방법 4 (화살표 함수 사용)
```js
var John = {
name: 'John',
getName : function () {
addGender = () => {
return this.name + ':남자'
};
return addGender();
}
};
var John = {
name: 'John',
getName : function () {
addGender = () => {
return this.name + ':남자' // 이 함수 내부의 this는 null이다. 그래서 바깥쪽 this를 찾는다.
};
console.log(this) // 여기에서 this는 John이다.
return addGender();
}
};
var John = {
name: 'John',
getName : () => {
addGender = () => {
return this.name + ':남자' // 이 함수 내부의 this는 null이다. 그래서 바깥쪽 this를 찾는다.
};
console.log(this) // 이 함수 내부의 this 또한 null이다. 그래서 바깥쪽 this를 찾는다. (window가 반환된다.)
return addGender();
}
};
function Person(name) {
this.name = name;
this.getName = function(){
return this.name;
};
};
var John = new Person('john');
John.getName(); // 1. 'john' 출력
John.__proto__ === Person.prototype; // 2. true 출력
Person === Person.prototype.constructor; // 3. true 출력
Person.__proto__ === Function.prototype // 4. true 출력
var myObj = { name: 'myObj' };
console.dir(myObj); // name 속성과 __proto__ 속성이 출력된다.
function add () {};
console.dir(Function);
console.dir(add);
add.__proto__ === Function.prototype;
add.bind === Function.prototype.bind
var john = new Person(‘john’); john.getName(); //john이 출력된다. getName 함수는 Person의 prototype에 정의되어있다. var jane = new Person(‘jane’); jane.getName(); //jane이 출력된다. john과 jane이 prototype을 통해 getName 함수를 사용한다.
- 참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
<br/>
<br/>
<br/>
### 부록 2. Lexical Scope
- JavaScript는 Lexical Scope(또는 Static Scope)를 따르고 있습니다.
- Lexical Scope란 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정하는 것입니다. 여기서 중요한 점은 함수의 호출 이 아니라 함수의 선언에 따라 결정된다는 점입니다.
### 예제
```js
var number = 1;
function a() {
var number = 10;
b();
}
function b() {
console.log(number);
}
a(); // ?
b(); // ?
함수의 호출에 따라 상위 스코프가 정해지는 것을 Dynamic Scope라고도 합니다. Perl, Bash Shell 등에서 Dynamic Scope를 따릅니다.
출처 : https://medium.com/@yeon22/javascript-lexical-scope-static-scope-and-dynamic-scope-c4a9e941fab3
var logger = {
name: "myLogger",
log: function(txt) {
console.log(this.name+":", txt);
}
};
logger.log("Hello there") // myLogger: Hello there
//You may want to pass log as a callback to forEach to log a whole array of elements:
var messages = ["first message", "second message", "third message"];
messages.forEach(function(msg) {
logger.log(msg);
});
//The simpler way to say that is:
messages.forEach(logger.log.bind(logger));
//messages.forEach(logger.log); // 이렇게 실행할 경우 this는 window가 됨.