1. this 키워드
메서드가 속한 객체를 가리키는 특수한 참조자
객체(Object) 안에 있는 메서드(Method)는 그 객체의 프로퍼티에 접근할 수 있다.
그런데 메서드가 프로퍼티에 접근하려면, 자신이 어느 객체에 속해 있는지 알아야 그 객체의 프로퍼티를 제대로 참조할 수 있다.
우리는 이 객체를 this 키워드를 통해 가리키게 된다.
2. 동작원리이해하기
(1) 객체리터럴방식
우리는 객체리터럴방식으로 JS에서 객체를 생성하였다.
객체리터럴방식은 중괄호{} 를 사용하여 객체를 정의하고 그 안에 프로퍼티와 메서드를 바로 정의하는 방식이었는데
이 방식을 사용하여 메서드 내부에서 객체를 가리키는 식별자를 바로 참조할 수 있다.
const circle = {
radius:5,
//프로퍼티: 객체 고유의 상태 데이터
getDiameter() {
return 2 * circle.radius;
}
//메서드: 상태 데이터를 참조하고 조작하는 동작
//이 메서드가 자신이 속한 객체의 프로퍼티나 다른 메서드를 참조하려면
// 자신이 속한 객체인 circle을 참조할 수 있어야 함
};
- 객체리터럴의 평가 완료되어 객체 생성
- circle 식별자에 생성된 객체가 할당
- getDiameter 메서드가 호출될 때는 circle 식별자 참조할 수 있음
생성자 함수 또한 자신이 생성할 인스턴스를 참조할 수 있어야 한다.
그런데 객체 리터럴은 변수에 할당되기 이전에 평가가 되고 식별자를 참조할 수 있다.
하지만 생성자 함수에 의한 객체 생성 방식은 생성자 함수를 정의한 이후 new 연산자와 함께 생성자 함수를 호출하는 단계가 추가로 필요하다.
(2) 생성자 함수에 의한 객체 생성방식
생성자 함수에 의한 객체는 정의하는 시점과 호출하는 시점이 나뉘어지는데
1️⃣ 정의하는 시점
// 생성자 함수를 정의하는 시점
function Person(name, age) {
this.name = name;
this.age = age;
}
2️⃣ 호출하는 시점
new 키워드를 사용할 때 생성자 함수가 실행이 되면서 새로운 객체가 비로소 생성이 되게 된다 !
// 생성자 함수를 호출하는 시점
const person1 = new Person('hyelong', 30);
const person2 = new Person('hihilong', 25);
따러서 정의하는 시점에는 아직 인스턴스를 생성하기 전이기 때문에 생성자 함수가 생성할 인스턴스를 가리키는 식별자를 알 수 없다.
따라서 자신이 속한 객체나 자신이 생성할 인스턴스를 가리킬 특수한 식별자가 필요하다 => this
📌 생성자함수방식에서 this 가 필요한 이유
객체 리터럴 방식에서는 객체를 직접 정의하므로, 이미 객체 자체가 명시적으로 존재한다.
객체 리터럴 내부에서 속성을 추가할 때는 이미 존재하는 객체에 속성이나 메서드를 바로 추가할 수 있다.
const person = { name: 'hilong', age: 30 };
// 여기서는 this가 필요하지 않음. 바로 name, age를 설정함.
생성자 함수는 일종의 템플릿으로서, 동일한 구조의 여러 객체를 동적으로 생성할 수 있어야 한다.
new 키워드로 호출될 때마다 새로운 객체가 만들어지고, 이 객체에 속성이나 메서드를 설정할 때 this가 필수적이다.
왜냐하면
this 는 새로 생성된 객체를 참조하는데
생성자 함수가 호출될 때마다 새로운 객체가 만들어지고 this 는 그 새로 생성된 객체를 가리키는 유일한 참조자 역할을 하기 때문임
또한..생성자 함수 내에서 어떤 객체가 만들어질지 미리 알 수 없기 때문에 this 를 통해 동적으로 생성된 객체에 접근해야만 객체의 속성이나 메서드를 설정할 수 있다.
3. this 바인딩
this 는 함수 호출방식에 의해 동적으로 결정됨
바인딩 : 식별자와 값을 연결하는 과정
//객체리터럴
const circle = {
radius:5,
getDiameter() {
return 2 * this.radius
}
};
//여기서 this 는 메서드를 호출한 객체 가리킴
//생성자 함수
function Circle(radius) {
this.radius = radius;
}
Circle.prototype.getDiameter = function () {
return 2 * this.radius;
};
//this 는 생성자 함수가 생성할 인스턴스를 가리킴
const circle = new Circle(5);
// 인스턴스 생성
=> 객체리터럴방식에서 this는 메서드를 호출한 객체, circle 을 가리키는데 생성자 함수에서는 생성자 함수가 생성할 인스턴스를 가리킨다.
즉... 생성자 함수 내에서 사용되는 this 는 이제 새로 생성된 객체를 가리키게 된다.
- this는 자기참조 변수이다. => 자신이 속한 객체나 자신이 생성할 인스턴스를 가리킨다
- this 는 코드 어디에서든 참조가능해서 전역에서도, 함수 내부에서도 참조할 수 있다.
- strict mode 에서도 this 바인딩에 영향을 준다.
- 전역에서 this는 전역객체 window를 가리킨다.
- strict mode 가 적용된 일반 함수 내부의 this 에서는 undefined 가 바인딩된다.
- => 일반 함수 내부에서 this 를 사용할 필요가 없어서
3-1. 일반 함수 호출
일반 함수로 호출된 모든함수(중첩함수, 콜백함수 포함) 내부의 this 에는 전역객체가 바인딩된다.
- 기본적으로 this에는 전역객체가 바인딩
- 객체를 생성하지 않는 일반 함수에서는 this 의미가 없음...
var value = 1; // 전역 변수 value 설정
const obj = {
value: 100, // 객체의 value 속성 설정
foo() {
console.log("foo's this:", this); // 여기서의 this는 obj를 가리킴
// 결과: {value:100, foo: f}
setTimeout(function () {
console.log("callback's this:", this); // 여기서의 this는 전역 객체(window)를 가리킴
console.log("callback's this.value", this.value); // 전역 객체의 value는 1
}, 100);
}
};
obj.foo(); // foo 메서드 호출
메서드 내부의 setTimeout 함수에 전달된 콜백함수의 this에는 전역객체가 바인딩된다.
따라서..this.value 는 obj 객체의 value 프로퍼티가 아니라 전역객체의 value 프로퍼티인 window.value 를 참조하게 된다.
만약에 메서드의 this 바인딩과 일치시키고 싶다면
var value = 1; // 전역 변수 value 설정
const obj = {
value: 100, // 객체의 value 속성 설정
foo() {
const that = this;
//this 바인딩(obj)을 변수에 할당함
setTimeout(function() {
console.log(that.valie);//100
},100};
}
};
이렇게 새로운 변수에 할당하여 정확하게 바인딩을 시킬 수 있도록 해줘야한다.
혹은 화살표 함수를 사용하여 화살표 함수 내부의 this는 상위스코프의 this를 가리키기 때문에,
this 바인딩을 정확하게 일치시킬 수 있다.
var value = 1;
const obj = {
value:100;
foo() {
setTimeout(() => console.log(this.value),100); //100
}
};
3-2. 메서드 호출
메서드 내부의 this에는 메서드를 호출한 객체, ,즉 메서드를 호출할 때 메서드 이름 앞의 마침표 연산자 앞에 기술한 객체가 바인딩된다.
const person = {
name="hihilong",
getName() {
return this.name;
}
};
console.log(person.getName());//hihilong
//메서드 getName을 호출한 객체는 person 임
person 객체의 프로퍼티가 가리키는 함수 객체는 person 객체에 포함된게 아니라
독립적으로 존재하는 별도의 객체다.
🚧 getName 프로퍼티가 함수 객체를 가리키고 있는 것 뿐이다.
따라서..이 프로퍼티가 다른 객체의 메서드가 될수도 있고 일반 ㅈ변수에 할당하면 일반 함수로 호출될수도있따.
- 메서드 내부의 this 는 프로퍼티로 메서드를 가리키는 객체와는 상관이 없고 메서드를 호출한 객체에 바인딩이된다.
- 프로토타입 메서드 내부에서 사용된 this도 일반 메서드와 마찬가지로 해당 메서드를 호출한 객체에 바인딩이된다.
function Person(name) {
this.name = name;
}
Person.prototype.getName = function () {
return this.name;
};
const me = new Person ('hihilong');
console.log(me.getName());
//hihilong
//getName 메서드를 호출한 객체는 me이다. .
//getName 메서드 내부의 this 는 me 를 가리키고 this.name 은 'hihilong' 이다.
Person.prototype.name = "hi";
console.log(Person.prototype.getName()); //hi
// getNAme 메서드를 호출한 객체는 person.prototype 인데 얘도 객체라서.. 직접 메서드를 호출할수있다
3-3 . 생성자 함수 호출
생성자 함수가 미래에 생성할 인스턴스가 바인딩된다.
new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수가 아니라 일반 함수로 동작한다.
const circle3 = Circle(15);
//new 연산자와 함께 호출하지 않으면 걍..일반함수의 호출임
//따라서 반환문이 없기 때문에 암묵적으로 undefined 반환함
//일반함수로 호출되었기 때문에 this 는 전역객체를 가리킨다.
3-4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출
apply, call, bind 메서드는 Function.protoytype 의 메서드다.
이들 메서드는 모든 함수가 상속받아 사용할 수 있다.
function getThisBinding() {
return this;
}
const thisArg = {a:1};
console.log(getThisBinding()); //window
console.log(getThisBinding.apply(thisArg));
//getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩
apply 와 call 메서드의 본질적인 기능은 함수를 호출한다.
함수를 호출하면 첫번째 인수로 전ㄴ달한 특정 객체를 호출한 함수의 this 에 바인딩한다.
- apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달
- call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로전달
- Function.prototype.bind메서드는 apply 와 call 과 달리 함수를 호출하지 않지만 첫번째 인수로 전ㄴ달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성에 반환함
- bind메서드는 this와 메서드 내부의 중첩 함수 또는 콜백함수의 this 가 불일치하는 문제를 해결하기 위해 유용하게 사용
const person = {
name:"hilong",
foo(callback);
setTimeout(callback,bind(this),100);
//bind메서드로 callback 함수 내부의 this 바인딩 전달
}
};
정리!
함수 호출 방식 | this 바인딩 |
일반함수호출 | 전역객체 |
메서드호출 | 메서드를호출한객체 |
생성자함수호출 | 생성자 함수가 미래에 생성할 인스턴스 |
Function.prototype.apply/call/bind메서드에 의한 간접호출 | Function.prototype.apply/call/bind메서드에 첫번째 인수로 전달한 객체 |
'Deep Dive 정리' 카테고리의 다른 글
[JS Deep Dive] 27장 - 배열 (1) (0) | 2024.10.31 |
---|---|
[JS Deep Dive] 24장 - 클로저(Closure) (1) | 2024.09.26 |
[JS Deep Dive] 20장 - strict mode (1) | 2024.09.11 |
[JS Deep Dive] 19장 - 프로토타입 (0) | 2024.09.11 |
[JS Deep Dive] 21장 - 빌트인객체 (0) | 2024.09.10 |