this
this 키워드는 JavaScript와 TypeScript에서 매우 중요한 개념으로, 어떻게 작동하는지를 이해하는 것은 필수!!
특히 this가 잘못 사용될 때 발생할 수 있는 문제들을 TypeScript를 통해 찾아낼 수 있습니다.
this와 화살표 함수 (this and Arrow Functions)
화살표 함수는 자신이 정의된 위치에서 this 값을 캡처합니다. 이는 함수가 호출되는 문맥과 관계없이 항상 동일한 this 값을 유지한다는 것을 의미합니다.
let deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function() {
return function() {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return { suit: this.suits[pickedSuit], card: pickedCard % 13 };
}
}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
alert("card: " + pickedCard.card + " of " + pickedCard.suit);
위 코드에서 createCardPicker 함수가 반환하는 함수 내부의 this는 deck 객체가 아닌 전역 객체를 가리킵니다. 이는 함수가 단순히 호출될 때 발생하는 현상입니다. 이 문제를 해결하려면 ES6의 화살표 함수를 사용하여 this를 올바르게 바인딩할 수 있습니다.
let deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function() {
// 화살표 함수로 `this`를 캡처
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return { suit: this.suits[pickedSuit], card: pickedCard % 13 };
}
}
};
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
console.log("card: " + pickedCard.card + " of " + pickedCard.suit);
위 예제에서 createCardPicker 메서드는 화살표 함수를 반환하여 this가 항상 deck 객체를 가리키도록 합니다!
this 매개변수 (this parameter)
TypeScript에서는 명시적으로 this 매개변수를 지정하여 함수가 호출될 때 this의 타입을 지정할 수 있습니다. 이를 통해 잘못된 this 사용을 방지할 수 있습니다.
this 매개변수는 함수의 매개변수 목록의 가장 앞에 오는 가짜 매개변수입니다!
function f(this: void) { // `this`를 사용할 수 없음 }
인터페이스를 사용하여 명확하고 재사용 가능한 타입을 정의할 수 있습니다.
interface Card {
suit: string;
card: number;
}
interface Deck {
suits: string[];
cards: number[];
createCardPicker(this: Deck): () => Card;
}
let deck: Deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
// `this: Deck`으로 명시적 타입 지정
createCardPicker: function(this: Deck) {
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return { suit: this.suits[pickedSuit], card: pickedCard % 13 };
}
}
};
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
console.log("card: " + pickedCard.card + " of " + pickedCard.suit);
createCardPicker 메서드가 Deck 타입의 this를 사용한다고 명시하고 있습니다. 이를 통해 this의 타입을 명확히 지정할 수 있고, 잘못된 호출을 방지할 수 있습니다!
콜백에서의 this 매개변수 (this parameters in callbacks)
콜백 함수에서 this를 사용할 때는 주의가 필요합니다. 콜백 함수는 종종 함수가 호출되는 문맥에서 this가 달라질 수 있기 때문입니다.
콜백 함수의 this는 일반 함수 호출로 인해 undefined가 될 수 있습니다. 이를 방지하려면 this 매개변수를 사용하여 타입을 명시할 수 있습니다.
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
class Handler {
info: string;
constructor(info: string) {
this.info = info;
}
onClickGood(this: void, e: Event) {
console.log('clicked!');
}
onClickBad(this: Handler, e: Event) {
this.info = e.type; // 잘못된 `this` 사용
}
}
let uiElement: UIElement = {
addClickListener(onclick) {
document.addEventListener('click', onclick);
}
};
let h = new Handler("Handler Info");
uiElement.addClickListener(h.onClickGood); // 올바른 사용
uiElement.addClickListener(h.onClickBad.bind(h)); // `.bind`를 사용하여 `this`를 바인딩
onClickGood 메서드는 this 타입을 void로 지정하여 this를 사용하지 않도록 합니다. onClickBad 메서드는 this를 Handler로 지정하고, .bind를 사용하여 this를 올바르게 바인딩하여 사용합니다.
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
class Handler {
info: string;
onClickBad(this: Handler, e: Event) {
// `this`를 사용할 수 있음
this.info = e.message;
}
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // 오류!
onClickBad 메서드는 Handler 객체의 인스턴스로 호출되어야 합니다. 이를 해결하려면 this 타입을 void로 변경합니다.
class Handler {
info: string;
onClickGood(this: void, e: Event) {
// `this`를 사용할 수 없음
console.log('clicked!');
}
}
let h = new Handler();
uiElement.addClickListener(h.onClickGood);
onClickGood 메서드는 this를 사용하지 않으므로 this 타입을 void로 지정할 수 있습니다. 만약 this를 사용하고 싶다면 화살표 함수를 사용할 수 있습니다.
class Handler {
info: string;
onClickGood = (e: Event) => { this.info = e.message; }
}
화살표 함수는 외부의 this를 캡처하므로, Handler 객체의 info 속성을 안전하게 사용할 수 있습니다. 이 경우, 화살표 함수가 객체마다 하나씩 생성된다는 점을 주의!!
Reference
https://typescript-kr.github.io/pages/functions.html
https://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/
'프로그래밍 > TypeScript' 카테고리의 다른 글
[TypeScript] 함수 오버로딩 (2) | 2024.07.18 |
---|---|
[TypeScript] 선택적 매개변수와 기본 매개변수 (0) | 2024.07.18 |
[TypeScript] 타입스크립트의 함수 정의와 타입 명시 (0) | 2024.07.17 |
[TypeScript] 리액트 프로젝트에서 타입스크립트 사용하기 (0) | 2024.07.16 |
[TypeScript] 변수 선언과 기본 타입 (2) | 2024.07.15 |