자바스크립트에서 객체는 상태를 나타내는
프로퍼티
, 동작을 나타내는메서드
로 묶인 복합적인 자료구조이다. 따라서 자신이 속한 객체의 프로퍼티를 참조하려면 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.
this
는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는자기 참조 변수
다. 프로퍼티, 메소드를 참조할 수 있다.
자바스크립트에서 암묵적으로 생성되기 때문에 함수 내부에서 arguments
객체를 지역 변수 처럼 쓰는것처럼 this
도 지역 변수
처럼 사용할 수 있다.
this
는 함수가 호출되는 방식에 따라 this
에 바인딩될 값이 동적으로 결정된다.// 생성자 함수
function Circle(radius) {
// this는 생성자 함수가 생성할 인스턴스를 가리킨다.
this.radius = radius;
}
Circle.prototype.getDiameter = function () {
// this는 생성자 함수가 생성할 인스턴스를 가리킨다.
return 2 * this.radius;
};
// 인스턴스 생성
const circle = new Circle(5);
console.log(circle.getDiameter()); // 10
생성자 함수 내부의 this
는 생성자 함수가 생성할 인스턴스를 가리킨다.
-> this
는 동적으로 결정된다.
this는 전역에서도 함수 내부에서도 참조할 수 있다.
// this는 어디서든지 참조 가능하다.
// 전역에서 this는 전역 객체 window를 가리킨다.
console.log(this); // window
function square(number) {
// 일반 함수 내부에서 this는 전역 객체 window를 가리킨다.
console.log(this); // window
return number * number;
}
square(2);
const person = {
name: 'Lee',
getName() {
// 메서드 내부에서 this는 메서드를 호출한 객체를 가리킨다.
console.log(this); // {name: "Lee", getName: ƒ}
return this.name;
}
};
console.log(person.getName()); // Lee
function Person(name) {
this.name = name;
// 생성자 함수 내부에서 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
console.log(this); // Person {name: "Lee"}
}
const me = new Person('Lee');
일반 함수 내부에서는 this
를 사용할 필요가 없기 때문에 undefined
가 바인딩된다.
this
는 객체의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수이므로 객체를 생성하지 않는 일반 함수에는 this
의 의미가 없다.함수의 상위 스코프를 결정하는
렉시컬 스코프
는 함수 정의가 평가되어 함수 객체가 생성되는 시점에 상위 스코프를 결정하지만this
바인딩은 함수 호출 시점에 결정된다.
동일한 함수도 다양한 방식으로 호출하기 때문에 크게 4가지로 나눠서 this
의 바인딩을 알아보자.
기본적으로 this에는
전역 객체
가 바인딩된다.
// var 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티다.
var value = 1;
// const 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티가 아니다.
// const value = 1;
const obj = {
value: 100,
foo() {
console.log("foo's this: ", this); // {value: 100, foo: ƒ}
console.log("foo's this.value: ", this.value); // 100
// 메서드 내에서 정의한 중첩 함수
function bar() {
console.log("bar's this: ", this); // window
console.log("bar's this.value: ", this.value); // 1
}
// 메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this에는 전역 객체가 바인딩된다.
bar();
}
};
obj.foo();
콜백 함수가 일반 함수로 호출된다면 콜백 함수 내부의 this
에도 전역 객체가 바인딩된다.
-> 어떤 함수라도 일반 함수로 호출되면 this
에 전역 객체
가 바인딩된다
메서드 내부의
this
에는 메서드를 호출한 객체, 즉 메서드를 호출할 때마침표(.)
연산자 앞에 기술한 객체가 바인딩된다.
주의할점: 메서드 내부의this
는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩된다.
const person = {
name: 'Lee',
getName() {
// 메서드 내부의 this는 메서드를 호출한 객체에 바인딩된다.
return this.name;
}
};
// 메서드 getName을 호출한 객체는 person이다.
console.log(person.getName()); // Lee
getName
메서드는 person
객체의 메서드로 정의되어 있다. 메서드는 프로퍼티에 바인딩된 함수다.
-> person
객체의 getName
프로퍼티가 가리키는 함수 객체는 person
객체에 포함된 것이 아니라 별도의 객체이고 getName
프로퍼티가 함수 객체를 가리키는 것 뿐이다.
getName
프로퍼티가 가리키는 함수 객체 즉getName
메서드는 다른 객체의 프로퍼티에 할당하는 것으로 다른 객체의 메서드가 될 수도 있고 일반 변수에 할당하여 일반 함수로 호출될 수도 있다.
생성자 함수 내부의
this
에는 생성자 함수가 (미래에) 생성할 인스턴스가 바인딩된다.
new
연산자와 함께 호출하면 생성자 함수로 동작한다.
// 생성자 함수
function Circle(radius) {
// 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
// 반지름이 5인 Circle 객체를 생성
const circle1 = new Circle(5);
// 반지름이 10인 Circle 객체를 생성
const circle2 = new Circle(10);
console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20
apply
,call
메서드는 함수를 호출하는 기능이다. 또한 첫 번째 인수로 전달한 특정 객체를 호출한 함수의this
에 바인딩한다.
function getThisBinding() {
console.log(arguments);
return this;
}
// this로 사용할 객체
const thisArg = { a: 1 };
// getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다.
// apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다.
console.log(getThisBinding.apply(thisArg, [1, 2, 3]));
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// {a: 1}
// call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다.
console.log(getThisBinding.call(thisArg, 1, 2, 3));
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// {a: 1}
apply
: 호출할 함수의 인수를 배열로 묶어 전달한다.
call
: 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다.
--> 대표적으로 slice
같은 메서드를 사용할때 쓰인다.
function convertArgsToArray() {
console.log(arguments);
// arguments 객체를 배열로 변환
// Array.prototype.slice를 인수없이 호출하면 배열의 복사본을 생성한다.
const arr = Array.prototype.slice.call(arguments);
// const arr = Array.prototype.slice.apply(arguments);
console.log(arr);
return arr;
}
convertArgsToArray(1, 2, 3); // [1, 2, 3]
bind
메서드는apply
,call
과 다르게 함수를 호출하지 않는다. 첫 번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다.
function getThisBinding() {
return this;
}
// this로 사용할 객체
const thisArg = { a: 1 };
// bind 메서드는 첫 번째 인수로 전달한 thisArg로 this 바인딩이 교체된
// getThisBinding 함수를 새롭게 생성해 반환한다.
console.log(getThisBinding.bind(thisArg)); // getThisBinding
// bind 메서드는 함수를 호출하지는 않으므로 명시적으로 호출해야 한다.
console.log(getThisBinding.bind(thisArg)()); // {a: 1}