자바스크립트에서 this는 호출 방식에 따라 바인딩 되는 값이 다릅니다. 자바스크립트 ES5에선 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 bind 메서드를 쓸 수 있습니다. 추가로 this 바인딩을 제공하지 않는 화살표 함수 또한 존재합니다.
1. 단독으로 쓴 this
this는 기본적으로 global object를 가리킵니다. 브라우저에서 호출할 경우 [object Window]가 되는걸 확인할 수 있습니다. 이는 ES5에서 추가된 strict mode에서도 마찬가지입니다.
'use strict';
var x = this;
console.log(x); //Window
2. 함수 안에서 쓴 this
함수 안에서 this는 함수를 호출하는 주인에게 바인딩됩니다. 여기서 말하는 함수의 주인은 window객체입니다.
function myFunction() {
return this;
}
console.log(myFunction()); //Window
let num = 0;
function addNum() {
this.num = 100;
num++;
console.log(num); // 101
console.log(window.num); // 101
console.log(num === window.num); // true
}
addNum();
위 코드에서 this.num의 this는 window객체를 가리킵니다. 따라서 num은 전역 변수를 가리키게 됩니다.
다만, strict mode에서는 함수 내의 this에 디폴트 바인딩이 없기 때문에 undefined가 됩니다.
"use strict";
function myFunction() {
return this;
}
console.log(myFunction()); //undefined
"use strict";
var num = 0;
function addNum() {
this.num = 100; //ERROR! Cannot set property 'num' of undefined
num++;
}
addNum();
따라서 this.num을 호출하면 undefined.num을 호출하는 것과 마찬가지기 때문에 에러가 납니다.
3. 메서드 안에서 쓴 this
메서드 호출 시 메서드 내부 코드에서 사용된 this는 해당 메서드를 호출한 객체로 바인딩됩니다.
let person = {
firstName: 'John',
lastName: 'Doe',
fullName: function () {
return this.firstName + ' ' + this.lastName;
},
};
person.fullName(); //"John Doe"
let num = 0;
function showNum() {
console.log(this.num);
}
showNum(); //0
var obj = {
num: 200,
func: showNum,
};
obj.func(); //200
4. 이벤트 핸들러 안에서 쓴 this
이벤트 핸들러에서 호출된 this는 이벤트를 받는 HTML 요소를 가리킵니다.
let btn = document.querySelector('#btn')
btn.addEventListener('click', function () {
console.log(this); //#btn
});
5. 생성자 안에서 쓴 this
생성자 안에서 호출된 this는 생성자 함수가 생성하는 객체로 바인딩됩니다.
function Person(name) {
this.name = name;
}
let kim = new Person('kim');
let lee = new Person('lee');
console.log(kim.name); //kim
console.log(lee.name); //lee
하지만 new 키워드를 안 쓴 경우 일반 함수 호출과 같아지기 때문에, 이 경우 this는 window에 바인딩됩니다.
let name = 'window';
function Person(name) {
this.name = name;
}
let kim = Person('kim');
console.log(window.name); //kim
6. 명시적 바인딩을 한 this
apply()와 call() 메서드는 Function Object에 기본적으로 정의된 메서드로, 인자를 this로 만들어주는 기능을 합니다.
function whoisThis() {
console.log(this);
}
whoisThis(); //window
let obj = {
x: 123,
};
whoisThis.call(obj); //{x:123}
apply()에서 매게변수로 받은 첫 번째 값은 함수 내부에서 사용되는 this에 바인딩되고, 두 번째 값인 배열은 자신을 호출한 함수의 인자로 사용합니다.
function Character(name, level) {
this.name = name;
this.level = level;
}
function Player(name, level, job) {
//this.name = name;
//this.level = level;
Character.apply(this, [name, level]);
this.job = job;
}
let me = new Player('Nana', 10, 'Magician');
call()도 apply()와 거의 같습니다. 차이점이 있다면 call()은 인수 목록을 받고, apply()는 인수 배열을 받는다는 차이가 있습니다. 공통적으로 둘다 함수를 호출한다는 공통점이 있습니다.
function Character(name, level) {
this.name = name;
this.level = level;
}
function Player(name, level, job) {
Character.call(this, name, level);
this.job = job;
}
let me = {};
Player.call(me, 'nana', 10, 'Magician');
7. 화살표 함수로 쓴 this
화살표 함수는 전역 컨텍스트에서 실행되더라도 this를 새로 정의하지 않고, 바로 바깥 함수나 클래스의 this를 씁니다.
let Person = function (name, age) {
this.name = name;
this.age = age;
this.say = function () {
console.log(this); // Person {name: "Nana", age: 28}
setTimeout(function () {
console.log(this); // Window
console.log(this.name + ' is ' + this.age + ' years old');
}, 100);
};
};
let me = new Person('Nana', 28);
me.say(); //global is undefined years old
위 코드를 보면 내부 함수에서 this가 전역 객체를 가리키는 바람에 의도와는 다른 결과가 나왔습니다.
let Person = function (name, age) {
this.name = name;
this.age = age;
this.say = function () {
console.log(this); // Person {name: "Nana", age: 28}
setTimeout(() => {
console.log(this); // Person {name: "Nana", age: 28}
console.log(this.name + ' is ' + this.age + ' years old');
}, 100);
};
};
let me = new Person('Nana', 28); //Nana is 28 years old
하지만 화살표 함수로 바꾸면 제대로 된 결과가 나오는걸 볼 수 있습니다.
참고
'면접질문' 카테고리의 다른 글
RESTful API (0) | 2022.10.28 |
---|---|
클로저(closure)의 개념 (0) | 2022.10.28 |
호이스팅(Hoisting)의 개념 (0) | 2022.10.25 |
브라우저 렌더링 원리 (0) | 2022.10.25 |