22장 : this
1. this 키워드
- this : 자신이 속한 객체 또는 생성할 인스턴스를 가리키는 자기 참조 변수
- this를 통해 자신이 속한 객체 또는 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.
const circle = {
radius: 5, // property
getDiameter() {
// method
// 이 method가 자신이 속한 객체의 property인 radius를 참조하려면
// 자신이 속한 객체인 circle을 참조할 수 있어야 한다.
return 2 * circle.radius;
},
};
console.log(circle.getDiameter()); // 10
- circle.radius 과 같이 자기 자신이 속한 객체를 재귀적으로 참조하는 방식은 일반적이지 않으며 바람직하지도 않음!
- this 를 사용하여 수정해보자!
const circle = {
radius: 5,
getDiameter() {
// this는 메서드를 호출한 객체인 circle을 가리킨다.
return 2 * this.radius;
},
};
console.log(circle.getDiameter()); // 10
- this.radius : 객체 리터럴의 메서드 내부에서의 this는 메서드를 호출한 객체(circle)를 가리킨다.
2. 함수 호출 방식과 this 바인딩
- this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다.
- 일반 함수 호출
- 메서드 호출
- 생성자 함수 호출
- applay/call/bind 에 의한 간접 호출
1. 일반 함수 호출
- 일반 함수로 호출 시 함수 내부의 this에는 전역 객체가 바인딩된다.
function foo() {
console.log("foo's this: ", this); // window
function bar() {
console.log("bar's this: ", this); //window
}
bar();
}
foo();
- 중첩 함수, 콜백 함수를 포함한 일반 함수로 호출된 모든 함수 내부의 this에는 전역 객체가 바인딩된다.
⭐ 메서드 내부의 중첩 함수나 콜백 함수의 this 바인딩을 메서드의 this 바인딩과 일치시키기 위한 방법
- 변수에 this를 할당하기
var value = 1;
const obj = {
value: 100,
foo() {
// this 바인딩(obj)을 변수 that에 할당한다.
const that = this;
// 일반 함수로 호출된 콜백 함수 내부의 this에는 전역 객체가 바인딩
setTimeout(function () {
console.log(this.value); // 1
console.log(that.value); // 100
}, 100);
},
};
- apply / call / bind 메서드를 사용한 명시적 바인딩
var value = 1;
const obj = {
value: 100,
foo() {
setTimeout(
function () {
console.log(this.value); // 100
}.bind(this),
100
);
},
};
- 화살표 함수 사용
var value = 1;
const obj = {
value: 100,
foo() {
// 화살표 함수 내부의 this는 상위 스코프의 this(obj)를 가리킨다.
setTimeout(() => console.log(this.value), 100); // 100
},
};
2. 메서드 호출
- 메서드 내부의 this는 메서드를 호출한 객체에 바인딩 된다.
- 메서드를 소유한 객체가 아님에 주의!
const person = {
name: "Lee",
getName() {
return this.name;
},
};
console.log(person.getName()); // Lee
const anotherPerson = {
name: "Kim",
};
// getName 메서드를 anotherPerson 객체의 메서드로 할당
anotherPerson.getName = person.getName;
// getName 메서드를 호출한 객체는 anotherPerson
console.log(anotherPerson.getName()); // Kim
// getName 메서드를 변수에 할당
const getName = person.getName;
// getName 메서드를 일반 함수로 호출
// 일반 함수로 호출된 getName 함수 내부의 this.name의 this는 전역 객체
console.log(getName()); // ''
- person.getName() : getName 을 호출한 객체가 person 이기 때문에 Lee 가 출력된다.
- getName() : 일반 함수로 호출 시 함수 내부 this(return this.name)에는 전역 객체가 binding 되므로 window.name의 값이 출력된다.
3. 생성자 함수 호출
- 생성자 함수 : 객체(인스턴스)를 생성하는 함수. new 연산자와 함께 호출하지 않으면 일반 함수로 동작한다.
- 생성자 함수 내부의 this에는 생성자 함수가 생성할 인스턴스가 바인딩된다.
function Circle(radius) {
// 생성자 함수 내부의 this는 생성자 함수가 생성할 객체(인스턴스)를 가리킨다
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
// radius = 5 인 Circle 객체 circle1 생성
const circle1 = new Circle(5);
console.log(circle1.getDiameter()); // 10
// new 연산자와 함께 호출하지 않아 일반 함수로 호출
const circle2 = Circle(15);
// 일반 함수 Circle은 return이 없으므로
console.log(circle2); // undefined
// 일반 함수로 호출된 Circle 내부의 this는 전역 객체를 가리킨다.
console.log(radius); // 15
- circle1.getDiameter() : new Circle(5) 내부의 this는 circle1 객체를 가리킨다.
- const circle2 = Circle(15) : 일반 함수로 호출된 Circle 내부의 this는 전역 객체를 가리킨다.
- console.log(radius) : 따라서 window.radius 의 값이 15가 된다.
4. apply / call / bind 메서드에 의한 간접 호출
- 해당 메서드는 Function.prototype의 메서드이므로 모든 함수가 상속받아 사용할 수 있다.
- 각 함수의 사용법 알아보기
- apply와 call 메서드는 함수를 호출하면서 첫 번째 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩한다.
- 두 메서드는 인수를 전달하는 방식만 다를 뿐 동일하게 동작한다.
- apply : 호출할 함수의 인수를 배열 하나로 전달한다. (ex. [1, 2, 3])
- call : 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다. (ex. 1, 2, 3)
- 두 메서드는 주로 arguments 객체(함수에 전달된 인수의 배열 형태의 객체)에 배열 메서드를 사용하기 위해 사용한다.
function getThisBinding() {
console.log(arguments);
return this;
}
// this로 사용할 객체
const thisArg = { a: 1 };
// getThisBinding 함수를 호출하면서 인수로 전달할 객체를 해당 함수의 this에 바인딩
console.log(getThisBinding.apply(thisArg, [1, 2, 3]));
console.log(getThisBinding.call(thisArg, 1, 2, 3));
// Arguments(3) [1, 2, 3, callee: f, Symbol(Symbol.iterator): f]
// { a: 1 }
- bind 메서드는 첫 번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다.
- bind 메서드는 메서드의 this와 메서드 내부의 중첩 함수 또는 콜백 함수의 this가 불일치할 때 유용하게 사용된다.
- 아래 예제는 콜백 함수가 일반 함수로서 호출되고 있다.
const person = {
name: "Lee",
foo(cb) {
// 현재 this는 foo 메서드를 호출한 person 객체
setTimeout(cb, 100);
},
};
person.foo(function () {
// 현재 this는 일반 함수로서 호출되었으므로 전역 객체
console.log(`my name is ${this.name}.`); // my name is .
});
- 위 예제를 bind 메서드를 사용하여 this의 바인딩을 교체할 수 있다.
const person = {
name: "Lee",
foo(cb) {
// bind 메서드로 콜백 함수의 this가 현재 시점의 this(person)로 바인딩된다.
setTimeout(cb.bind(this), 100);
},
};
person.foo(function () {
console.log(`my name is ${this.name}.`); // my name is Lee.
});
Deep Dive Study week - 01
'[Study] Deep Dive 스터디' 카테고리의 다른 글
[JS] 이터러블 (0) | 2024.01.23 |
---|---|
[JS] 프로미스 (0) | 2024.01.22 |
[JS] 비동기 프로그래밍 (0) | 2024.01.20 |
[JS] ES6 함수의 추가 기능 (0) | 2024.01.16 |
[JS] 클로저 (0) | 2024.01.14 |