내장 클래스 확장
: 배열, 맵과 같은 내장 클래스의 확장
// 메서드 하나를 추가합니다(더 많이 추가하는 것도 가능).
class PowerArray extends Array {
isEmpty() {
return this.length === 0;
}
}
let arr = new PowerArray(1, 2, 5, 10, 50);
alert(arr.isEmpty()); // false
let filteredArr = arr.filter(item => item >= 10);
alert(filteredArr); // 10, 50
alert(filteredArr.isEmpty()); // false
if (arr.constructor === PowerArray) {
// true
}
class PowerArray extends Array {
isEmpty() {
return this.length === 0;
}
// 내장 메서드는 반환 값에 명시된 클래스를 생성자로 사용합니다.
static get [Symbol.species]() {
return Array;
}
}
let arr = new PowerArray(1, 2, 5, 10, 50);
alert(arr.isEmpty()); // false
// filter는 arr.constructor[Symbol.species]를 생성자로 사용해 새로운 배열을 만듭니다.
let filteredArr = arr.filter(item => item >= 10);
// filteredArr는 PowerArray가 아닌 Array의 인스턴스입니다.
alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function
내장 객체와 정적 메서드 상속
: 내장객체는 Object.keys, Array,isArray 등의 자체 정적 메서드를 가짐
: 내장 클래스는 정적 메서드를 상속 받지 못함
instanceof 연산자
: 객체가 특정 클래스에 속하는지 확인
obj instanceof Class
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
Symbol.hasInstance
: 프로토타입 체인을 거슬러 올라가며 인스턴스 여부나 상속 여부를 확인하는 instanceof와 달리
직접 확인 로직을 설정할 수 있는 메서드
: InstanceOf의 동작을 커스터마이징 할 때 사용
objA.isPrototypeOf(objB)
: objA가 objB의 프로토타입 체인 상 어딘가에 있으면 true를 반환
Object.prototype.toString
: 타입 확인을 위한 메서드
: [object object] 대신 정확한 타입을 출력
let obj = {};
alert(obj); // [object Object]
alert(obj.toString()); // 같은 결과가 출력됨
// 편의를 위해 toString 메서드를 변수에 복사함
let objectToString = Object.prototype.toString;
// 아래 변수의 타입은 무엇일까요?
let arr = [];
alert( objectToString.call(arr) ); // [object Array]
let s = Object.prototype.toString;
alert( s.call(123) ); // [object Number]
alert( s.call(null) ); // [object Null]
alert( s.call(alert) ); // [object Function]
Symbol.toStringTag
: toString의 동작을 커스터마이징 하는 메서드
// 특정 호스트 환경의 객체와 클래스에 구현된 toStringTag
alert( window[Symbol.toStringTag]); // Window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest
alert( {}.toString.call(window) ); // [object Window]
alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]
믹스인
: 자바스크립트는 단일 상속만을 허용하는 언어로, 객체에는 단 하나의 [[Prototype]]만 있을 수 있고
클래스는 클래스 하나만 상속 받을 수 있음
: 다른 클래스를 상속 받을 필요 없이 이들 클래스에 구현되어 있는 메서드를 담고 있는 클래스
// 믹스인
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!
믹스인 안에서 믹스인 상속 받기
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!
이벤트 믹스인
: 믹스인은 이벤트를 생성하는 메서드 (.trigger(name, [...data])를 제공
: 메서드 .on(name, handler)은 name에 해당하는 이벤트에 리스너로 handler 함수를 추가
: .on은 이벤트가 트리거 될 때 호출되고 .trigger 호출에서 인수를 얻음
: 메서드 .off(name, handler)는 handler 리스너를 제거
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");
Refference
내장 클래스 확장하기
ko.javascript.info
'instanceof'로 클래스 확인하기
ko.javascript.info
믹스인
ko.javascript.info
'Programming > JavaScript' 카테고리의 다른 글
모던 자바스크립트 (에러핸들링) 8-2. 커스텀 에러와 에러 확장 (0) | 2022.04.23 |
---|---|
모던 자바스크립트 (에러핸들링) 8-1. try catch (0) | 2022.04.23 |
모던 자바스크립트 (클래스) 7-2. 정적 메서드와 정적 프로퍼티 / private, protected 프로퍼티와 메서드 (0) | 2022.04.23 |
모던 자바스크립트 (클래스) 7-1. 클래스 기본 문법 / 클래스 상속 (0) | 2022.04.23 |
모던 자바스크립트 (프로토타입과 프로토타입 상속) 6-2. 내장 객체의 프로토타입 / 프로토타입 메서드와 __proto__가 없는 객체 (0) | 2022.04.23 |