티스토리 뷰
9.6 Class checking: "instanceof"
the instanceof operator
instanceof 연산자는 obj가 Class에 속하거나 Class를 상속받는 클래스에 속하면 true를 반환한다. (자바와 기능이 같다.)
class Rabbit {}
let rabbit = new Rabbit();
// rabbit이 클래스 Rabbit의 객체인가요?
alert( rabbit instanceof Rabbit ); // true
생성자 함수나 내장 클래스에도 사용 가능하다.
// 클래스가 아닌 생성자 함수
function Rabbit() {}
alert( new Rabbit() instanceof Rabbit ); // true
// ---------------
let arr = [1, 2, 3];
alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true
obj instanceof Class의 내부 동작 및 커스텀
클래스에 Symbol.hasInstance가 구현되어 있으면 instanceof 구문이 실행될 때 해당 메서드를 호출한다. 그 결과는 boolean 타입이어야 하고, 이 메서드를 커스터마이징 함으로써 instanceof의 동작을 정의할 수도 있겠다.
// canEat 프로퍼티가 있으면 animal이라고 판단할 수 있도록
// instanceOf의 로직을 직접 설정합니다.
class Animal {
static [Symbol.hasInstance](obj) {
if (obj.canEat) return true;
}
}
let obj = { canEat: true };
alert(obj instanceof Animal); // true, Animal[Symbol.hasInstance](obj)가 호출됨
하지만, 대부분의 클래스엔 해당 메서드가 구현되어 있지 않은데, 이 경우엔 Class.prototype이 obj의 프로토타입 체인 상에서 일치하는 프로토타입이 있는지 확인하여 결과를 반환한다.
obj.__proto__ === Class.prototype?
obj.__proto__.__proto__ === Class.prototype?
obj.__proto__.__proto__.__proto__ === Class.prototype?
...
// 이 중 하나라도 true라면 true를 반환합니다.
// 그렇지 않고 체인의 끝에 도달하면 false를 반환합니다.
Bonus: Object.prototype.toString for the type
일반 객체를 문자열로 변환하면, [object Object]가 된다. 하지만, 아래 예시처럼 사용하면 결과가 조금 달라진다.
// 편의를 위해 toString 메서드를 변수에 복사함
let objectToString = Object.prototype.toString;
// 아래 변수의 타입은 무엇일까요?
let arr = [];
alert( objectToString.call(arr) ); // [object Array]
- 숫자형 – [object Number]
- 불린 형 – [object Boolean]
- null – [object Null]
- undefined – [object Undefined]
- 배열 – [object Array]
- 그 외 – 커스터마이징 가능
이후 부분은 데코레이터, 포워딩 챕터를 추가로 보고 다시 살펴봐야겠다.
Symbol.toStringTag
아래와 같이 커스텀할 수도 있다. 이러한 방법들을 통해 typeof, instanceof을 대체할 수도 있겠다.
let user = {
[Symbol.toStringTag]: "User"
};
alert( {}.toString.call(user) ); // [object User]
9.7 Mixins
javascript도 Java처럼 단일 상속만을 허용한다. 이럴 때, 믹스인이라는 개념을 도입해 좀 더 다양한 설계를 해볼 수 있겠다.
아래처럼 메서드가 여러 개가 담긴 객체를 하나 만드는 것이다.
// 믹스인
let sayHiMixin = {
sayHi() {
alert(`Hello ${this.name}`);
},
sayBye() {
alert(`Bye ${this.name}`);
}
};
// 사용법:
class User {
constructor(name) {
this.name = name;
}
}
// 메서드 복사
Object.assign(User.prototype, sayHiMixin);
// 이제 User가 인사를 할 수 있습니다.
new User("Dude").sayHi(); // Hello Dude!
믹스인 안에서 믹스인 상속을 사용하는 것도 가능하다. (__proto__ 부분이 믹스인 상속에 대한 내용 같다... 추후 다시 봐야 할 듯)
let sayMixin = {
say(phrase) {
alert(phrase);
}
};
let sayHiMixin = {
__proto__: sayMixin, // (Object.create를 사용해 프로토타입을 설정할 수도 있습니다.)
sayHi() {
// 부모 메서드 호출
super.say(`Hello ${this.name}`); // (*)
},
sayBye() {
super.say(`Bye ${this.name}`); // (*)
}
};
class User {
constructor(name) {
this.name = name;
}
}
// 메서드 복사
Object.assign(User.prototype, sayHiMixin);
// 이제 User가 인사를 할 수 있습니다.
new User("Dude").sayHi(); // Hello Dude!
Event mixin
아래는 실제 브라우저에서 이벤트가 발생했을 때, 사용할 수 있는 믹스인 예시이다. 코드를 천천히 따라가 보면서 만들어보는 게 좋겠다.
let eventMixin = {
/**
* 이벤트 구독
* 사용패턴: menu.on('select', function(item) { ... }
*/
on(eventName, handler) {
if (!this._eventHandlers) this._eventHandlers = {};
if (!this._eventHandlers[eventName]) {
this._eventHandlers[eventName] = [];
}
this._eventHandlers[eventName].push(handler);
},
/**
* 구독 취소
* 사용패턴: menu.off('select', handler)
*/
off(eventName, handler) {
let handlers = this._eventHandlers?.[eventName];
if (!handlers) return;
for (let i = 0; i < handlers.length; i++) {
if (handlers[i] === handler) {
handlers.splice(i--, 1);
}
}
},
/**
* 주어진 이름과 데이터를 기반으로 이벤트 생성
* 사용패턴: this.trigger('select', data1, data2);
*/
trigger(eventName, ...args) {
if (!this._eventHandlers?.[eventName]) {
return; // no handlers for that event name
}
// 핸들러 호출
this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));
}
};
// 클래스 생성
class Menu {
choose(value) {
this.trigger("select", value);
}
}
// 이벤트 관련 메서드가 구현된 믹스인 추가
Object.assign(Menu.prototype, eventMixin);
let menu = new Menu();
// 메뉴 항목을 선택할 때 호출될 핸들러 추가
menu.on("select", value => alert(`선택된 값: ${value}`));
// 이벤트가 트리거 되면 핸들러가 실행되어 얼럿창이 뜸
// 얼럿창 메시지: Value selected: 123
menu.choose("123");
'스터디 > JavaScript' 카테고리의 다른 글
모던 자바스크립트 튜토리얼 part1 - 8.2 ~ 3 (0) | 2024.05.27 |
---|---|
모던 자바스크립트 튜토리얼 part1 - 8.1 (0) | 2024.05.24 |
모던 자바스크립트 튜토리얼 part1 - 9.3 ~ 4 (0) | 2024.05.24 |
모던 자바스크립트 튜토리얼 part1 - 9.2 (0) | 2024.05.24 |
모던 자바스크립트 튜토리얼 part1 - 9.1 (0) | 2024.05.23 |