getDiamter 메서드 내에서 자신이 속한 객체를 가르키는 식별자 circle을 참조하고 있다. 이 참조 표현식이 평가되는 시점은 getDiameter 메서드가 호출되어 함수 몸체가 실행되는 시점이다.
위 예제의 객체 리터럴은 circle 변수에 할당하기 직전에 평가된다. 자기 자신이 속한 객체를 재귀적으로 참조하는 방식은 일방적이지 않으며 바람직하지도 않다.
function Circle(radius) {
// 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가르키는 식별자를 알 수 없다.
????.radius = radius;
}
Circle.protoype.getDiameter = function () {
// 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가르키는 식별자를 알 수 없다.
return 2 * ????.radius
}
// 생성자 함수로 인스턴스를 생성하려면 먼저 생성자 함수를 생성해야 한다.
const circle = new Circle(5);
생성자 함수를 정의하는 시점에는 아직 인스턴스를 생성하기 이전이므로 생성자 함수가 생성할 인스턴스를 가르키는 식별자를 알 수 없다.
따라서 자신이 속한 객체 또는 자신이 생성할 인스턴스을 가리키는 특수한 식별자가 필요하다 . 이를 위해 자바스크립트는 this라는 특별한 식별자를 제공한다.
this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가르키는 자기 참조 변수다. this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.
this 키워드느 자바스크립트에 의해 암묵적으로 생성되며, 코드 어디서든 참조할 수 있다. this가 가르키는 값, 즉 this 바인딩은 함수 호출 방식에 의해 동적으로 결정된다
this 바인딩 - 식별자와 값을 연결하는 과정을 바인딩이라 하고, this 바인딩은 this와 this를 가르킬 객체를 바인딩 하는 과정이다
자바스크립트에서 this는 함수가 호출되는 방식에 따라 this에 바인딩 될 값 즉 this 바인딩이 동적으로 결정된다.
함수 호출방식과 this 바인딩
this 바인딩은 함수 호출 방식, 즉 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다.
렉시컬 스코프 this 바인딩 - 함수 상위 스코프를 결정하는 방식인 렉시컬 스코프는 함수 정의가 평가되어 함수 객체가 생성되는 시점에 상위 스코프를 결정한다. 하지만 this 바인딩은 함수 호출 시점에 결정된다.
주의사항으로는 함수도 다양한 방식으로 호출이 가능하다는 점인데, 함수를 호출하는 방식은 크게 4가지가 있다
일반 함수 호출
메서드 호출
생성자 함수 호출
Function.prototype.apply/call/bind 메서드에 의한 간접 호출
const foo = function() {
console.dir(this);
}
// 1. 일반 함수 호출
// foo 함수 내부의 this는 전역 객체
foo(); // window;
// 2. 메서드 호출
// foo 함수의 프로퍼티 값으로 할당하여 호출
// foo 함수 내부의 this는 메서드를 호출한 객체 obj를 가르킨다
const obj = { foo }
// 3. 생성자 함수 호출
// foo 함수를 new 연산자와 함께 생성자 함수로 호출
// foo 함수 내부의 this는 생성자 함수가 생성한 인스턴스를 가르킴
new foo(); // foo {}
// 4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출
// foo 함수 내부의 this는 인수에 의해 결정됨
const bar = { name : 'bar' };
foo.call(bar); // bar
foo.apply(bar); // bar
일반 함수에 의한 호출
기본적으로 this는 전역객체가 바인딩된다.
아래 예제처럼 일반 함수 중첩함수를 호출하면 함수 내부의 this에는 전역 객체가 바인딩 된다.
일반함수에서 this는 의미가 없다.
function foo() {
console.log(this); // window;
function bar() {
console.log(this); // window;
}
bar();
}
foo();
일반 함수로 호출된 모든 함수 내부의 this에는 전역 객체가 바인딩 된다.
매서드에 의한 호출
메서드 내부의 this에는 메서드를 호출한 객체, 즉 메서드를 호출할 떄의 메서드 이름 앞에 마침표(.) 연산자 앞에 기술한 객체가 바인딩 된다. 주의할 것은 내부의 this는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩 된다는 것이다.
const Person = {
name : 'Lee',
getName() {
// 메서드 내부의 this는 메서드를 호출한 객체에 바인딩 된다
return this.name;
}
}
console.log(Person.getName(()); // Lee
위 예제를 보면 getName 메서드 내에서는 Person 객체의 메서드로 정의되어있다. 메서드는 프로퍼티에 바인됭 함수다
즉 person 객체의 getName 프로퍼티가 가르키는 함수 객체는 person 객체에 포함된 것이 아니라 독립적으로 존재하는 별도의 객체다. getName 프로퍼티가 함수 객체를 가르키고 있을 뿐이다.
따라서 getName 프로퍼티가 가르키고 있는 함수 객체 즉, getName 메서드는 다른 객체의 프로퍼티에 할당하는 것으로 다른 객체의 메서드가 될 수 있고 일반 변수에 할당하여 일반 함수로 호출도 가능하다.
메서드 내부의 this는 프로퍼티로 메서드를 가르키고 있는 객체와는 관계가 없고 메서드를 호출한 객체에 바인딩 된다.
function Person(name) {
this.name = name;
}
Person.prototype.getName = function() {
return this.name;
}
const me = new Person('Lee');
console.log(me.getName()); // Lee
Person.prototype.name = 'Kim';
console.log(Person.prototype.getName()); // Kim
위 예제에서 getName 메서드를 호출한 객체는 me다.
getName 메서드 내부의 this는 me를 가르키며 this.name은 Lee다
생성자 함수 호출
생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스가 바인딩된다.
function Circle(radius) {
// 생성자 함수의 내부의 this는 생성자 함수가 생성할 인스턴스를 가르킨다
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
const circle1 = new Circle(5);
console.log(circle1.getDiameter()); // 10