일반적인 객체지향 언어에서의 this는 자신을 가리키는 참조변수이지만, Javascript의 this는 어디에서 호출하는 방법 에 따라 달라집니다. 이번 포스팅에서는 this를 호출을 하느냐에 따라 값들이 어떻게 달라지는지 알아보겠습니다.
console.log(window===this); //true
function func(){
console.log(this); // window
function inner(){
console.log(this);//window
}
inner();
}
func();
'use strict';
console.log(this); // undefined
지금부터는 this가 window객체가 아닌 경우를 알아보겠습니다.
var name='nodejs';
function func() {
let obj = {
name : 'javascript',
sayName: function(){
console.log(this);
console.log(`window is this?? ${window===this}`);
console.log(this.name);
}
};
console.log(this);
console.log(`window is this?? ${window===this}`);
console.log(this.name);
console.log('===================');
obj.sayName();
}
func();
위와같이 작성한 소스 코드에서 로그에서는 어떻게 내용들을 불러올까요?
이럴때는 함수를 어디에서 호출을 했느냐? 를 바탕으로 이해를 하시면 빠를꺼 같습니다.
2.그다음에는 obj.sayName함수를 호출합니다. 위에서 말했다시피, 함수를 누가 호출을 했나? 를 바탕으로 접근하면 obj라는 객체에서 함수를 호출했다는것을 확인할수 있습니다. 그래서 sayName 함수 안의 this는 obj객체를 가리키고, 아래의 로그인 window is this??는 false를 , this.name은 jasvascript를 출력합니다.
call,apply,bind은 명시적으로 this를 넘겨주는 함수 메소드 입니다. 이때 값을 어떻게 넣어주냐에 따라 this의 값이 달라집니다.
var name='iron man';
function a(){
console.log(this.name);
}
a(); //iron man(아무것도 넘기지않았기 떄문에 window객체가 자동적으로 넘어감);
a.call({'name':'captain'}); //window 대신 {'name':'captain'}객체가 넘어감., captain
a.apply({'name':'black widow'}); //black widow
a.bind({'name':'thor'}).call(); // thor
function Hero(name,skill){
this.name = name;
this.skill = skill;
}
Hero.prototype.useSkill = function(){
console.log(`${this.name} use skill ${this.skill}`);
}
Hero('iron man','mark1');
console.log(window.name,window.skill);//iron man mark 1
console.log(this.name,this.skill); // iron man, mark1
let captain=new Hero('captain america','shield');
console.log(captain.useSkill()); //captain america use skill shield
위에 Hero라는 생성자 함수를 선언합니다. 그런데 아래와 같이 Hero를 선언 후에, 로그로 name과 skill값을 찍어내면, 일반 함수에서는 this를 window객체로 간주하기 떄문에 window.name, window.skill과 똑같이 나옵니다.
이를 막기 위해서 new 생성자 키워드를 이용하여, this가 가리키는 대상을 인스턴스 생성자 자체가 됩니다.
function Obj(){
this.age=0; //this는 Obj의 인스턴스.
setInterval(function(){
this.age++;
console.log(this.age); // NaN??
},1000);
}
let obj=new Obj();
Obj라는 클래스(객체) 정의한 후에, obj이름을 가진 인스턴스를 생성하였습니다. obj 인스턴스는 1초마다, age를 +1씩 해주는 작업을 수행합니다. 과연 로그는 어떻게 나올까요? 결과는 NaN이 나옵니다. 왜냐하면 setInterval에서 실행 된 코드는 호출 된 함수가 아닌 별도의 실행 컨텍스트에서 실행됩니다. 즉, 생성자 객체에서 실행된것이 아니기 때문에, 이때 this는 window객체와 똑같다는 결론이 나옵니다.
function Obj(){
this.age=0; //this는 Obj의 인스턴스.
let that = this;
setInterval(function(){
that.age++;
console.log(that.age);
},1000);
}
let obj=new Obj();
위의 소스코드는 setInterval안에서 Object 객체에 접근을 하기위해서 블록변수 that을선언하여, Obj객체 생성자 값을 할당하여 접근하고있는것을 알 수 있습니다.
화살표 함수가 등장하기 전에는 변수하나를 선언해서, 생성자 함수 this에 할당한후, setInterval에서 할당한 that에 age를 증분을 시켜주고 있습니다. 하지만, 화살표 함수로 아래와 같이 해결할 수가 있습니다.
function Obj(){
this.age=0; //this는 Obj의 인스턴스.
setInterval(()=>{
this.age++;
console.log(this.age);
},1000);
}
let obj=new Obj();
화살표 함수는 자기자신의 고유한 this값을 가지고 있지 않고, 둘러싸는 렉시컬 범위(lexical scope)의 this가 사용됩니다. 즉, 화살표함수 바깥 범위의 this값을 자연스럽게 가지고 들어옵니다.