[JS] 7. 객체지향 프로그래밍 (OOP)
객체지향 프로그래밍 (Object-Oriented Programming) : 원시 타입을 제외한 모든 것이 객체
자바스크립트는 프로토타입 (
Prototype) 기반 객체지향 프로그래밍 언어 (Lisp→Scheme→JavaScript)
- 객체 타입 (
Object Type)과 클래스 (Class)를 통해 생성된 인스턴스 타입 (Instance Type) →this - 프로퍼티는 각 인스턴스의
EnvRec에 생성되고, 메소드는 프로토타입에 할당되어 모든 인스턴스가 동일- 특정 인스턴스에 종속되지 않는 함수는 정적인 클래스 메소드 (
Class Method)
- 특정 인스턴스에 종속되지 않는 함수는 정적인 클래스 메소드 (
class Animal {}
class Dog extends Animal {}
const lucy = new Dog();
console.log(lucy instanceof Dog); // true
console.log(lucy instanceof Animal); // true
console.log(lucy instanceof Object); // true
console.log(Object.getPrototypeOf(lucy)); // Animal {}
console.log(Object.getPrototypeOf(Dog)); // [class Animal]
console.log(Object.getPrototypeOf(Animal)); // {}
- 프로토타입은 단방향
LinkedList으로 연결된 구조 → 프로토타입 체인 (Prototype Chain)- 특정 프로퍼티에 접근할 때 해당 객체에 프로퍼티가 없으면, 내부 참조를 따라 상위 프로토타입을 순차적으로 검색
정적 필드 & 메소드 (Static Field & Method) : 특정 인스턴스와 무관하게 클래스에 존재하는 메소드
- 인스턴스 프로퍼티를 참조할 수 없음 →
Static영역에 생성
class Animal {
static ID = 1;
static isDog(ani) {
return ani.name === 'Dog';
}
}
const dog = new Animal('Dog');
dog.isDog(dog); // TypeError: dog.isDog is not a function
Animal.isDog(dog); // OK
싱글톤 패턴 (
Singleton Pattern) : 어떤 클래스가 한 번만 인스턴스화되어, 그 인스턴스에 대한 전역 접근 제공class Singleton { static #_instance; // 관례상 protected constructor() { if (Singleton.#_instance) return Singleton; this.name = 'Singleton'; Singleton.#_instance = this; } static getInstance() { return this.#_instance || new this(); } } const s1 = new Singleton(); const s2 = new Singleton();
접근자 (Accessor) 프로퍼티 : 특정 인스턴스와 무관하게 클래스에 존재하는 메소드
- 접근자 프로퍼티을 통해 함수를 프로퍼티처럼 활용할 수 있음
class Emp1 {
set fullName(name) { // set을 프로퍼티처럼 활용할 수 있음 → accessor
[this.firstName, this.lastName] = name.split(' ');
}
get fullName() { // get을 프로퍼티처럼 활용할 수 있음 → accessor
return `${this.firstName} ${this.lastName}`;
}
}
class Emp2 { // stackOverflow 발생!
set name(nm) { this.name = nm; }
get name() { return this.name; }
}
class Emp3 {
#name;
set name(#name) { this.#name = name; }
get name() { return this.name; }
}
const hong1 = new Emp1();
hong1.fullName = 'Gil-Dong Hong';
console.log(hong1.fullName);
const hong2 = new Emp2();
hong2.fullName = 'Gil-Dong Hong';
console.log(hong2.fullName);
const hong3 = new Emp3();
hong3.fullName = 'Gil-Dong Hong';
console.log(hong3.fullName);
프록시 (Proxy) 객체 : 객체의 특정 동작을 가로채서 (Hooking)해서 추가 동작 수행
- 객체의 작업에 대한 기록 → 로깅 (
Logging) - 잘못된 접근에 대한 오류 (
Validation Check) - 기능 제어 (읽기 전용 등) 및 객체 정보 숨기기
const obj = {
id: 1,
name: 'Hong',
f() {
console.log('fffff')
},
};
export const objProxy = new Proxy(obj, {
set(target, prop, value){
console.log('proxy.set>>', prop, value);
target[prop] = value;
},
get(target, prop){
console.log('proxy.get >> ', prop);
return target[prop];
},
});
objProxy.id = 100;
console.log('obj.id>>', objProxy.id, obj['id']);
상속 (Inheritance) : SubClass가 프로토타입 체인으로 Superclass의 모든 데이터와 기능을 상속
class Dog extends Animal {
constructor(name) {
super(name); // 필수(for chaining) + 중복(overload) 불가!
}
bark() {
console.log('bowwow!');
}
}
- 모든 객체는 프로토타입 체인 (
__proto__) 이라는 내부 속성을 가짐- 객체를 생성하는 생성자 함수는
prototype속성을 가짐 → 해당 프로토타입이 객체의 프로토타입가 됨 - 하위 객체는 상위 객체의 프로토타입을 따라 상위 객체의 속성 및 메소드를 탐색
- 객체를 생성하는 생성자 함수는
스택 (
Stack) & 큐 (Queue) : 데이터를 저장하고 관리하기 위한 자료구조class Stack { // 스택 자료구조 collection = []; push(value) { this.collection.push(value); } pop() { return this.collection.pop(); } } const stack = new Stack(); stack.push(1); // 추가하기 stack.push(2); // 추가하기 stack.push(3); // 추가하기 console.log(stack.pop()) // 마지막으로 추가된 하나 꺼내기 class Queue { // 큐 자료구조 collection = []; enqueue(value) { this.collection.push(value); } dequeue() { return this.collection.shift(); } } const queue = new Queue(); queue.enqueue(1); // 추가하기 queue.enqueue(2); // 추가하기 queue.enqueue(3); // 추가하기 console.log(queue.dequeue()) // 마지막으로 추가된 하나 꺼내기
메소드 오버라이딩 (Method Overriding) : 자식 클래스가 부모 클래스의 메소드를 재정의
class Animal {
...
id = 1; // 멤버 프로퍼티
#age = 10; // 멤버 프로퍼티 (protected)
toString() { // 객체의 toString() 오버라이딩 ([메소드] 다형성!)
return `This animal's name is ${this.name}.`;
}
}
다중 상속 (Multiple Inheritance) : 한 클래스가 여러 클래스들을 상속
- 자바스크립트는 단일 상속 언어 →
Mixin을 통해 제한적으로 구현할 수 있음 (타입스크립트 :Interface)
// Mixin 정의
const myMixin = {
sayHello() {
console.log("Hello!");
},
sayGoodbye() {
console.log("Goodbye!");
},
};
function MyClass() {
... // 클래스의 생성자 로직
}
Object.assign(MyClass.prototype, myMixin); // Mixin 적용
const obj = new MyClass(); // 객체 생성 및 Mixin 메서드 호출
obj.sayHello(); // 출력: Hello!
obj.sayGoodbye(); // 출력: Goodbye!