javascript에서 함수의 this
키워드는 다른 언어들과 비교하여 조금 다르게 동작한다. 또한 strict mode
와 non-strict mode
사이에서도 조금 다르다.
대부분의 경우, this
의 값은 함수를 호출하는 방법에 의해 결정된다. 실행하는 동안의 할당에 의해 설정될 수 없고, 함수가 호출될 때 마다 다를 수 있다. ES5는 함수의 this
값이 함수가 어떻게 호출되었는지 개의치 않고 설정할 수 있는 bind
메소드를 소개했다.
this
의 값은 어떻게 변화할까? this
가 어떤 값과 연결되는 지는 this
의 바인딩을 통해서 확인해 볼 수 있다. 바인딩이란, this
의 호출 방식에 따라서 this
가 특정 '객체'에 연결되는 것이다. this
의 바인딩은 일반 함수 내부, 메서드 내부, 생성자 함수 내부, Call, Apply, Bind
를 통한 '호출 방식'으로 나눠서 살펴볼 수 있다.
this
는 글로벌 객체와 바인딩된다.this
는 메서드를 호출한 객체와 바인딩된다.this
는 생성자 함수가 생헐할 인스턴스와 바인딩된다.this
는 글로벌 객체와 바인딩된다.console.log(this === window); // true;
a = 30;
console.log(window.a); // 30
function x() {
return this;
}
x() === window; // true
this
는 메서드를 호출한 객체와 바인딩된다.let ryan = {
firstName: "Ryan",
lastName: "Kim",
driveCar() {
console.log(`${this.firstName} drives a car.`)
}
}
ryan.driveCar(); // 'Ryan drives a car.'
this
는 생성자 함수가 생성할 인스턴스와 바인딩된다.function person() {
this.firstName = "Ryan",
this.lastName = "Kim",
this.start = function() {
console.log(`${this.firstName} drives a car.`)};
};
let person1 = new person();
console.log(person1); // person { firstName: 'Ryan', lastName: 'Kim', start: ƒ (), __proto__: person { constructor: ƒ person() } }
Call
과 Apply
메서드는 기본적으로 함수를 호출하는 역할을 한다. 그렇다면 기존 함수 호출과의 차이점은 무엇일까? 그것은 바로, 해당 메서드를 사용해 함수를 '실행'하면, 함수의 첫 번째 인자로 전달하는 객체에 this
를 '바인딩'할 수 있다는 것이다. 이를 통해서 유사 배열 arguments
객체에 배열 메서드를 사용할 수 있다. 반면에, bind
는 첫 번째 인자를 this
에 바인딩하지만 함수를 실행하지 않으며, 새로운 함수를 반환한다.
Call
을 사용하면, 함수를 실행하고 함수의 첫 번째 인자로 전달하는 값에 this
를 바인딩한다.
function logName(a, b, c) {
console.log(this.name);
console.log(this.nationality);
console.log(a + b + c);
}
const person = {
name: 'Ryan',
nationality: 'South Korea'
}
logName.call(person, 1, 2, 3 );
// 'Ryan'
// 'South Korea'
// 6
apply
를 사용하면 함수를 실행하고 함수의 첫 번째 인자로 전달하는 값에 this
를 바인딩한다. call
과의 차이점은 인자를 배열의 형태로 전달한다는 것이다. 이 때, 인자로 배열 자체가 전달하는 것이 아니라 배열의 요소들이 값으로 전달된다.
function logName(a, b, c) {
console.log(this.name);
console.log(this.nationality);
console.log(a + b + c);
}
const person = {
name: 'Ryan',
nationality: 'South Korea'
}
const nums = [1, 2, 3];
logName.apply(person, nums );
// 'Ryan'
// 'South Korea'
// 6
// 요즘에는 apply를 사용하지 않고, spread operator와 call을 활용한다.
logName.call(person, ...nums);
// 'Ryan'
// 'South Korea'
// 6
bind
는 함수의 첫 번째 인자에 this
를 바인딩한다는 점은 같지만, 함수를 실행하지 않고, 새로운 함수를 반환한다. 즉, 반환된 새로운 함수를 실행해야 원본 함수가 실행된다.
function logName(a, b, c) { // 원본 함수
console.log(this.name);
console.log(this.nationality);
console.log(a + b + c);
}
const person = {
name: 'Ryan',
nationality: 'South Korea'
}
const Ryan = logName.bind(person, 1); // 새로운 함수
Ryan(2, 3);
// 'Ryan'
// 'South Korea'
// 6
Call
메서드는 함수를 실행하고, 첫 번째 인자에 this
를 바인딩하며, 이후의 값을 함수의 인자로 전달한다. Apply
메서드는 함수를 실행하고, 첫 번째 인자에 this
를 바인딩하며, 이후의 값을 배열의 형태로 받아 차례로 함수의 인자로 전달한다. bind
메서드는 함수를 실행하지 않으며, 첫 번째 인자에 this
를 바인딩한 새로운 함수를 반환한다. 각 메서드를 통해 전달할 수 있는 인자의 갯 수에는 제한이 없다.
이처럼 this
는 함수 호출 방식에 따라서 동적으로 결정된다. 예시에서 살펴본 것처럼 함수를 일반 함수로 호출할 경우, this
는 글로벌 객체를, 메서드로 호출할 경우 이를 호출한 객체를, 생성자 함수를 호출할 경우, 생성자 함수가 생성할 인스턴스를 가리키게 된다. Call, Apply, Bind 메서드 사용 시, 메서드에 첫 번째 인수로 전달하는 객체에 바인딩 된다.