this는 '이것'을 뜻하는 JS 키워드이다. 이건 무슨 뜬구름 잡는 소리야? MDN에 정의된 this는 다음과 같다.
대부분의 경우 this의 값은 함수를 호출한 방법에 의해 결정됩니다.
그렇다 this는 함수를 호출할때 값이 결정된다. 이걸 좀 더 알아들을수 있게 바꾼다면... this는 누가 나를 불렀니? 라 할 수 있을 것이다.
메서드가 어디서 정의되었는지에 상관없이 this는 ‘점 앞의’ 객체가 무엇인가에 따라 ‘자유롭게’ 결정됩니다.
함수가 실행되는 방법은 다음과 같다.
Function 호출
func();
Method 호출
String.prototype.toString();
new 키워드를 이용한 호출(생성자 호출)
const child = new Parents();
이때 this는 호출되는 함수안에 존재하고, 위와 같은 방법으로 호출될때 그 형태가 결정되는 것이다.
단순한 함수 호출에서 this는 얼핏 생각해 보면 함수 그 자체를 의미할 것 같지만, 비 엄격 모드에선 전역 객체를 참조하게 되며(window or global), 엄격 모드에선 정의되지 않은 값으로 결정 된다(undefined). function 호출 방법을 통해 함수를 실행 할때는 this를 사용할 의미가 없다. 만일 전역 this를 호출하고 싶은 것이라면...
globalThis 프로퍼티는 코드가 실행중인 현재 컨텍스트와 관계 없이 항상 전역 객체를 얻을 수 있다.
함수를 어떤 객체의 메서드 호출하면 this의 값은 그 객체를 사용한다.(this는 그 객체에 바인딩 된다.) 코드를 통해 알아보자.
//객체 리터럴로 객체를 정의 객체는 프로퍼티로 age와 name을 가지고 있고 메서드로 getAge를 가지고 있다.
let obj = {
age: 30,
name: 'joe',
getAge: function() {
console.log(`${this.name} is ${this.age} years old`)
}
}
obj.getAge(); // joe is 30 years old
obj.getAge();는 obj 객체의 getAge 메서드를 호출한다. getAge 메서드가 실행되는 순간 this는 obj로 바인딩 되며, this.name은 obj.name으로 this.age는 obj.age로 치환된다.
함수를 new 키워드와 함께 생성자로 사용하면 this는 새로 생긴 객체에 묶인다.(this는 새로 생긴 인스턴스에 바인딩 된다.) 코드를 통해 알아보자
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
getAge() {
return `${this.name} is ${this.age} years old`
}
}
let joe = new Person('joe', 30);
joe.getAge(); // joe is 30 years old
joe.name // 'joe'
joe.age // 30
let joe = new Person('joe', 30)은 전달인자를 각각 매개변수 name과 age로 전달하여 new 키워드와 함께 생성자로 사용한 함수다. (class문법 도입전에는 동일한 결과를 얻기 위해 function으로 정의했었다. 다시 말해 class는 function이다.) 생성자를 통해 Person class의 새로운 인스턴스인 joe가 만들어졌고 joe의 name은 'joe', joe의 age는 30으로 할당 되었다. 여기서 생성자의 this와 메서드의 this 모두 joe로 바인딩 된다. 따라서 this.name은 joe.name으로 this.age는 joe.age로 치환된다.
getAge 메서드의 this는 getAge를 실행할때 joe로 바인딩 된다.
MDN에 정의된 call은 다음과 같다.
MDN의 예제를 따라가며 좀 더 상세히 이해해 보자.
1 function Product(name, price) {
2 this.name = name;
3 this.price = price;
4 }
5
6 function Food(name, price) {
7 Product.call(this, name, price);
8 this.category = 'food';
9 }
10
11 console.log(new Food('cheese', 5).name);
12 // expected output: "cheese"
this는 함수가 실행되는 순간에 값이 결정된다. call을 사용한 예제에서 this가 어떤 값을 갖게 될까?
정리해 보면 call은 첫번째 인자에 this를 바인딩하는 역할을 하고 두번째 인자부터 그대로 대상 함수로 전달하는 역할을 한다.
apply는 call과 동일한 역할을 하는 메서드이다. call과의 차이는 두번째 인자에 무엇을 받는가이다. apply는 두번째 인자에 인수 배열 하나를 받는다.
func.apply(this, [5, 10]);
얼핏 생각해 보면 bind 메서드 또한 call과 동일한 역할을 하는것 같다. 하지만 큰 차이점이 있는데 바로 bind 메서드는 새로운 함수를 생성한다는 점이다.
bind() 함수는 새로운 바인딩한 함수를 만듭니다. 바인딩한 함수는 원본 함수 객체를 감싸는 함수로, ECMAScript 2015에서 말하는 특이 함수 객체exotic function object입니다. 바인딩한 함수를 호출하면 일반적으로 래핑된 함수가 호출 됩니다.
MDN 예제로 더 자세히 이해해 보자
1 this.x = 9;
2 var module = {
3 x: 81,
4 getX: function() { return this.x; }
5 };
6
7 module.getX();
8
9 var retrieveX = module.getX;
10 retrieveX();
11
12 var boundGetX = retrieveX.bind(module);
13 boundGetX();
setTimeout 메서드(전역 객체의 메서드)에서 this는 기본적으로 window(or global)에 바인딩 된다. 따라서 클래스 인스턴를 참조하는 this를 필요로 하는 경우에는 bind를 사용하면 setTimeout의 this를 특정 객체를 참조할 수 있도록 고정시킬수 있다.
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
};
화살표 함수엔 this가 없습니다. 화살표 함수 본문에서 this에 접근하면, 외부에서 값을 가져옵니다.
let group = {
title: "1모둠",
students: ["보라", "호진", "지민"],
showList() {
this.students.forEach(
student => alert(this.title + ': ' + student)
);
}
};
group.showList();