자바스크립트 코드를 보시면 생성자 함수나 객체 메서드 내부에 this 라는 키워드를 자주 봅니다. 오늘은 this 에 대해서 알아보도록 하겠습니다.
this 의 정의는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수 입니다. this 를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있습니다.
자바스크립트의 this 는 함수가 호출되는 방식에 따라서 this에 바인딩될 값이 결정 됩니다. 자바스크립트의 this 는 동적으로 결정 됩니다.
사실 this 는 코드 어디에서나 사용 가능 합니다. 하지만 this 의 목적은 객체의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수이므로 객체의 메서드 내부 또는 생성자 함수 내부에서만 의미가 있습니다.
만약 객체 내부, 생성자 함수 내부 이외의 위치에서 this 를 참조한다면 this 에는 전역 객체가 바인딩 됩니다. 전역객체가 바인딩 된 this로 어떤 변수를 참조한다면 만약 전역 스코프에 해당 전역 변수가 선언되어 있다면 그 변수를 참조하겠지만 없다면 브라우저에서는 "" 이고 node.js 에서는 undefined 가 반환 됩니다.
일반 함수에서의 this 호출은 어떠한 위치의 함수라도 모두 this 에는 전역 객체가 바인딩 됩니다. 그 함수가 객체 내부의 메서드 내부의 중첩함수 또는 콜백 함수라도 모두 this 는 전역 객체를 가리키게 됩니다.
코드로 확인 해 봅시다.
const kane = {
name: "kane",
age: 3,
hobby : function() {
console.log(`안녕하세요 저의 이름은 ${this.name} 입니다. 저의 취미는 축구 입니다.`);
function anotherHobby() {
console.log(`안녕하세요 저의 이름은 ${this.name} 입니다. 저의 또 다른 취미는 축구 입니다.`);
}
anotherHobby();
}
}
kane.hobby();
//안녕하세요 저의 이름은 kane 입니다. 저의 취미는 축구 입니다.
//안녕하세요 저의 이름은 입니다. 저의 또 다른 취미는 축구 입니다.
위의 코드와 같이 kane 이라는 객체 내부의 hobby 메서드가 있고 메서드 내부에 또 다른 anotherHobby 함수를 정의하고 호출했지만 anotherHobby 내부에서의 this 의 바인딩된 객체는 전역 객체라서 this.name 이 "" 으로 된 것을 확인 할 수 있습니다. 콜백함수도 위의 결과와 같습니다.
메서드 내부의 this에는 메서드를 호출한 객체가 this 에 바인딩 됩니다. 메서드를 소유하고 있는 객체가 메서드 내부의 this가 절대 아닙니다.
정확히 말하자면 메서드는 객체의 소유가 아닙니다. 객체의 프로퍼티가 함수를 가리키고 있는 것 입니다. 함수는 객체이기 때문에 독립적인 객체 형태로 존재 합니다.
코드로 확인 해 봅시다.
const kane = {
name: "kane",
age: 3,
hobby : function() {
console.log(`안녕하세요 저의 이름은 ${this.name} 입니다. 저의 취미는 축구 입니다.`);
}
}
kane.hobby();
// 안녕하세요 저의 이름은 kane 입니다. 저의 취미는 축구 입니다.
위의 코드에서 kane 객체 내부의 hobby 메서드를 kane 객체로 호출했기 때문에 메서드 내부의 this 는 kane 이 되어 this.name 은 kane 이 된 것 입니다.
여기서 위 kane 객체를 그림으로 나타내면
위와 같이 될 것 입니다. 독립적인 함수의 객체가 존재하는 것 입니다.
이 말은 저 함수는 다른 객체의 메서드가 될수도 있고 일반 변수에 할당되어 일반 함수로 호출 될 수 있다는 뜻 입니다.
한번 해볼까요?
코드로 확인 해 보겠습니다.
const kane = {
name: "kane",
age: 3,
hobby : function() {
console.log(`안녕하세요 저의 이름은 ${this.name} 입니다. 저의 취미는 축구 입니다.`);
}
}
const tom = {
name : "tom",
}
tom.hobby = kane.hobby;
const hobby = kane.hobby;
tom.hobby();
// const kane = {
name: "kane",
age: 3,
hobby : function() {
console.log(`안녕하세요 저의 이름은 ${this.name} 입니다. 저의 취미는 축구 입니다.`);
}
}
const tom = {
name : "tom",
age : 6,
}
tom.hobby = kane.hobby;
const hobby = kane.hobby;
tom.hobby();
// 안녕하세요 저의 이름은 tom 입니다. 저의 취미는 축구 입니다.
hobby();
// 안녕하세요 저의 이름은 입니다. 저의 취미는 축구 입니다.
위와 같이 kane 객체 내부의 hobby 메서드를 tom 객체의 메서드로 할당했고 hobby 변수에 할당해서 호출 해 보았습니다.
호출 결과를 보시면 tom 객체로 호출한 hobby 메서드의 this 는 tom 객체를 가리키고 this.name 은 tom 인 것을 확인 할 수 있다.
하지만 일반 변수에 할당하고 호출하면 this 는 전역 객체를 가리키기 때문에 "" 가 출력 됩니다.
그림으로 보면
두 객체의 hobby 프로퍼티는 모두 하나의 함수 객체를 가리키게 됩니다.
생성자 함수에서 내부에서의 this 는 생성되는 인스턴스를 가리키게 됩니다.
const a = {
sayHobby : function() {
console.log("저의 취미는 축구 입니다.");
},
};
const a = {
sayHobby() {
console.log("저의 취미는 축구 입니다.");
},
};
두번째 방법은 첫번째 방법과 같은 기능을 합니다.
" this가 없는 화살표 함수" 입니다.
코드를 살펴 보면
const a = {
name : "kane",
age : 10,
sayHobby : function() {
const sayRealHobby = function() {
console.log(this.name + " 의 취미는 축구 입니다.");
};
sayRealHobby();
},
};
a.sayHobby(); // 의 취미는 축구 입니다.
위의 코드처럼 작성하고 a.SayHobby(); 로 호출하면 과연 결과는 어떻게 출력 될까요? "kane 의 취미는 축구 입니다." 로 출력 될까요?
결과는 " 의 취미는 축구 입니다." 로 출력 됩니다. 그 이유는 sayRealHobby 함수를 호출할때의 객체는 전역 객체인 window 인데 전역객체에서 name 이라는 프로퍼티가 없기 때문입니다.
그럼 this가 a가 되게 하려면 어떻게 해야 할까요?
const a = {
name : "kane",
age : 10,
sayHobby : function() {
const thisObject = this;
const sayRealHobby = function() {
console.log(thisObject.name + " 의 취미는 축구 입니다.");
};
sayRealHobby();
},
};
a.sayHobby(); // kane 의 취미는 축구 입니다.
위와 같이 sayHobby 메서드를 호출한 객체가 a 이기 때문에 메서드 내의 this 는 a 객체 입니다. this 를 변수에 저장하여 sayHobby 내부 함수인 sayRealHobby 에서 변수로 a 객체의 name 프로퍼티 값을 사용할 수 있습니다. 하지만 이 방법은 일부러 객체를 다른 변수에 저장하여 사용해야 하기 때문에 불편함이 있습니다. 또 다른 방법은 화살표 함수를 사용하는 것 입니다.
const a = {
name : "kane",
age : 10,
sayHobby : function() {
const sayRealHobby = () => {
console.log(this.name + " 의 취미는 축구 입니다.");
};
sayRealHobby();
},
};
위와 같이 화살표 함수를 사용하면 "kane 의 취미는 축구 입니다." 로 출력 됩니다. 어떻게 가능했던 걸까요?
그 이유는 화살표 함수에는 this가 없기 때문 입니다.
화살표 함수가 호출될 때 this 를 사용하면 this 가 가리키는 것이 없기 때문에 그 위에 상위 환경에서 this를 찾는 겁니다. 위의 코드에서는 this 가 a 인 것 입니다. 그래서 this.name 의 값은 "kane"이 된 것이죠.
화살표 함수에서의 this 는 자신이 속한 객체를 가리킵니다.
html 파일에서 button 태그를 이용해 버튼을 생성하고 버튼을 a 객체의 clickButton 메서드를 호출하는 코드를 작성해 보았습니다. 이때 clickButton 메서드가 실행되면서 출력으로 나오는 this 는 무엇을 가리킬까요? a 객체를 가리킬까요?
const $button = document.querySelector('button');
$button.addEventListener('click', a.clickButton);
const a = {
name : "kane",
clickButton : function() {
console.log(this.name +" 이 버튼을 클릭하였습니다.");
},
}
this 가 가리키는 것은 clickButton 메서드를 호출한 button 태그 입니다.
이렇게 this 는 함수가 호출될때 결정되기 때문에 계속 바뀌게 됩니다. 그럼 이 this 를 내가 원하는 객체를 가리키도록 할수는 없을까요? 이 방법을 해결하기 위한 함수가 바로 bind 입니다. bind 함수의 첫번째 인자 값으로 this 에 고정 시키고 싶은 객체를 넘겨주면 이제 그 메서드를 어떤 객체에서 호출하든 this 는 항상 bind 함수의 인자로 넘긴 객체 입니다.
코드로 살펴 봅시다.
const a = {
name : "kane",
age : 10,
sayHobby : function() {
console.log(this.name+" 의 취미는 축구 입니다.");
},
};
const b = a.sayHobby;
b(); // 의 취미는 축구 입니다.
const c = a.sayHobby.bind(a);
c(); // kane 의 취미는 축구 입니다.
위와 같이 a 객체의 sayHobby 메서드를 b 변수에 할당하고 호출하면 b 객체가 sayHobby 메서드를 호출한 것이기 때문에 this 는 b 객체가 되어 name 이라는 프로퍼티를 찾을 수 없어 "" 가 출력 됩니다. 하지만 c 변수에 bind 함수를 사용해서 sayHobby 메서드를 할당하고 호출 했을때 this 는 a 객체가 됩니다.
화살표 함수에서는 bind 함수를 이용해서 this 에 객체를 고정시킬 수 없습니다.