객체지향 프로그래밍(object-oriented programming)
- 소프트웨어를 설계하고 구축하기 위한 컴퓨터 프로그래밍 패러다임(Paradigm) 중 하나로, 소프트웨어를 현실 세계처럼 생각하고 만드는 것.
- 현실 세계의 개념을 모델링하고 코드를 구조화하는 방법으로, 코드의 재사용성과 유지보수성을 높임.
클래스(class; 객체 생성자 함수)
- 객체지향 프로그래밍에서, 클래스는 데이터와 메서드(함수)를 포함하는 추상적인 설계 청사진(blueprint)을 의미함.
- 클래스를 기반으로 실제 데이터를 담는 객체를 생성할 수 있음. 객체는 클래스의 인스턴스(instance)라고도 불림.
- 즉, 객체는 현실 세계의 개념 또는 데이터를 나타내며, 클래스는 이러한 객체를 만들기 위한 템플릿이라고 할 수 있음.
- 클래스(class)는 추상적이고 범용적임. (어떤 자동차)
- 인스턴스(instance)는 구체적이고 한정적임. (특정 자동차)
- 클래스는 계층적으로 분류. (슈퍼클래스; superclass, 서브클래스; subclass)
- 운송수단이라는 클래스 아래 자동차, 비행기, 보트, 오토바이 클래스가 존재한다면,
- 운송수단은 자동차의 슈퍼클래스이고 자동차는 운송수단의 서브클래스.
- 또한 서브클래스가 서브클래스를 가질 수 있음.
- 클래스의 식별자는 대문자로 시작해 구분해 줌.
instanceof
연산자를 이용해 인스턴스의 소속여부를 알 수 있음.
- 클래스의 설계 의도대로 사용한다면, 데이터 프로퍼티는 항상 인스턴스에서 정의해야 함. (프로토타입 체인에서 정의 지양)
- 하지만 강제하는 장치가 없으므로, 명확하게 확인하기 위해
hasOwnProperty
사용 권장.
hasOwnProperty
에 대한 내용.
function nirvana() {}
function queen() {}
const x = new nirvana();
console.log(x instanceof nirvana);
console.log(x instanceof queen);
ES5
- 클래스는 함수 형태.
new
키워드로 인스턴스 생성.
function Animal(type, name, sound) {
this.type = type;
this.name = name;
this.sound = sound;
this.say = function () {
console.log(this.sound);
};
}
const dog = new Animal('개', '멍멍이', '멍멍');
const cat = new Animal('고양이', '야옹이', '야옹');
dog.say();
cat.say();
ES6
calss
키워드로 클래스 생성.
- 단축 문법이며 기존의 클래스의 개념이 변한 것이 아님.
constructor
키워드로 생성자 함수를 구분.
new
키워드로 인스턴스 생성.
class Animal {
constructor(type, name, sound) {
this.type = type;
this.name = name;
this.sound = sound;
this.say = function () {
console.log(this.sound);
};
}
}
const dog = new Animal('개', '멍멍이', '멍멍');
const cat = new Animal('고양이', '야옹이', '야옹');
dog.say();
cat.say();
클래스 상속
- 상속은 클래스가 다른 클래스로부터 속성과 메서드를 상속받는 것을 의미.
- 서브클래스를 만들 수 있고,
- 인스턴스 또한 클래스의 모든 기능을 상속.
- 클래스에 계층이 생기므로 적절한 위치에 메서드의 정의가 중요.
ES5
function Animal(type, name, sound) {
this.type = type;
this.name = name;
this.sound = sound;
}
Animal.prototype.say = function () {
console.log(this.sound);
};
function Dog(name, sound) {
Animal.call(this, '개', name, sound);
}
function Cat(name, sound) {
Animal.call(this, '고양이', name, sound);
}
Dog.prototype = Animal.prototype;
Cat.prototype = Animal.prototype;
const dog = new Dog('멍멍이', '멍멍');
const cat = new Cat('야옹이', '야옹');
ES6
extends
키워드를 이용해 상속.
constructor
를 선언하는 과정에서 super
키워드를 이용해서 슈퍼클래스의 생성자를 호출 및 기본값 설정.
class Animal {
constructor(type, name, sound) {
this.type = type;
this.name = name;
this.sound = sound;
}
say() {
console.log(this.sound);
}
}
class Dog extends Animal {
constructor(name, sound) {
super('개', name, sound);
}
}
class Cat extends Animal {
constructor(name, sound) {
super('고양이', name, sound);
}
}
const dog = new Dog('멍멍이', '멍멍');
const cat = new Cat('야옹이', '야옹');
dog.say();
cat.say();
class Food {
constructor(name) {
this.name = name;
this.brands = [];
}
addBrand(brand) {
this.brands.push(brand);
}
print() {
console.log(`"${this.name}" 판매 음식점: ${this.brands.join(', ')}.`);
}
}
const pizza = new Food('피자');
pizza.addBrand('파파존스');
pizza.addBrand('도미노피자');
console.log(pizza);
const chicken = new Food('치킨');
chicken.addBrand('푸라닭');
chicken.addBrand('교촌치킨');
console.log(chicken);
pizza.print();
chicken.print();
- 클래스에 계층이 생기므로 적절한 위치에 메서드의 정의가 중요.
- 슈퍼클래스로 운송수단이 있고, 서브클래스로 자동차와 보트가 있을 때
- deployAirbags(에어백 작동) 클래스는 보트에 있을 필요가 없으니 자동차에 배치하고,
- addPassenger(탑승자 추가) 클래스는 자동차와 보트에 존재가능 하기때문에 슈퍼클래스에 배치.
class Vehicle {
constructor() {
this.passengers = [];
console.log('Vehicle created');
}
addPassenger(p) {
this.passengers.push(p);
}
}
class Car extends Vehicle {
constructor() {
super();
console.log('Car created');
}
deployAirbags() {
console.log('BWOOSH!');
}
}
const v = new Vehicle();
v.addPassenger('Hong');
v.addPassenger('Jung');
console.log(v.passengers);
const c = new Car();
c.addPassenger('Park');
c.addPassenger('Kim');
console.log(c.passengers);
c.deployAirbags();
v.deployAirbags();
메서드
인스턴스 메서드(프로토타입)
- 클래스의 인스턴스에서 사용할 수 있는 인스턴스 메서드.
- 모든 함수에는
prototype
이라는 특별한 프로퍼티가 존재.
- 객체 내장 메서드와 프로퍼티는 주로 프로토타입 체인으로 상속.
- 일반적인 함수에서는 사용할 일이 없지만, 클래스에서는 중요함.
- 클래스로 만든 인스턴스에서 재사용(공유)하는 값 또는 함수를 설정하는 것.
- 특히
new
키워드로 새 인스턴스를 만들었을 때 매우 중요함.
- 새로운 인스턴스는 클래스의
prototype
프로퍼티를 인스턴스의 __proto__
프로퍼티에 저장.
- 인스턴스에서 메서드나 프로퍼티를 정의하면 프로토타입을 가리는 효과가 있음.
- JS의 우선순위가 인스턴스 => 프로토타입이기 때문. (동적 디스패치; 동적 메서드 호출)
ES5
function Animal(type, name, sound) {
this.type = type;
this.name = name;
this.sound = sound;
}
Animal.prototype.say = function () {
console.log(this.sound);
};
const dog = new Animal('개', '멍멍이', '멍멍');
const cat = new Animal('고양이', '야옹이', '야옹');
dog.say();
cat.say();
ES6
- 클래스 안(constructor 밖)에서 함수를 만들게 되면, 자동으로 프로토타입이 됨.
class Animal {
constructor(type, name, sound) {
this.type = type;
this.name = name;
this.sound = sound;
}
say() {
console.log(this.sound);
}
}
const dog = new Animal('개', '멍멍이', '멍멍');
const cat = new Animal('고양이', '야옹이', '야옹');
dog.say();
cat.say();
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
this._userGears = ['P', 'N', 'R', 'D'];
this._userGear = this._userGears[0];
}
get userGear() {
return this._userGear;
}
set userGear(value) {
if (this._userGears.indexOf(value) < 0) {
throw new Error(`Invalid gear: ${value}`);
}
}
shift(gear) {
this._userGear = gear;
}
}
const car1 = new Car('Tesla', 'Model S');
const car2 = new Car('Mazda', 'Mx5-rf');
car1.shift('D');
car2.shift('R');
console.log(car1.userGear);
console.log(car2.userGear);
클래스 메서드
- 인스턴스에 적용되지 않는 정적 메서드.
this
가 인스턴스가 아니라 클래스에 묶임.
- 정적 메서드에서
this
대신 클래스 이름을 사용하는 것이 상기하기 쉬움.
- 클래스에 관련되지만 인스턴스와는 관련이 없는 범용적인 작업에 사용.
- 예를 들어 자동차 차대번호(VIN)를 중복없이 부여하는 메서드.
- 인스턴스 내에서는 다른 인스턴스와 중복여부를 확인해 부여하기 힘듬.
ES6
class Car {
static getNextVin() {
return Car.nextVin++;
}
constructor(make, model) {
this.make = make;
this.model = model;
this.vin = Car.getNextVin();
}
static areSimilar(car1, car2) {
return car1.make === car2.make && car1.model === car2.model;
}
static areSame(car1, car2) {
return car1.vin === car2.vin;
}
}
Car.nextVin = 0;
const car1 = new Car('Tesla', 'X');
const car2 = new Car('Mazda', 'MX-5');
const car3 = new Car('Mazda', 'MX-5');
console.log(car1.vin);
console.log(car2.vin);
console.log(car3.vin);
console.log(Car.areSimilar(car1, car2));
console.log(Car.areSimilar(car2, car3));
console.log(Car.areSame(car2, car3));
console.log(Car.areSame(car2, car2));
다형성(polymorphism)
- 객체지향 언어에서 여러 슈퍼클래스의 멤버인 인스턴스를 가리킴.
- 대부분의 객체지향 언어에서 다형성은 특별한 경우에 속하지만,
- JS는 어디서든 객체를 쓸 수 있어, 어떤 면에서는 JS의 객체는 모두 다형성을 가짐.
- 또한 JS의 모든 객체는 루트 클래스인 Object의 인스턴스고,
toString
같은 중요한 메서드를 상속하기 위해.
class Vehicle {
constructor() {}
}
class Car extends Vehicle {
constructor() {
super();
}
}
class Boat extends Vehicle {
constructor() {
super();
}
}
const v = new Vehicle();
const c = new Car();
const b = new Boat();
console.log(v instanceof Object);
console.log(c instanceof Object);
console.log(b instanceof Object);
console.log(v instanceof Vehicle);
console.log(c instanceof Vehicle);
console.log(b instanceof Vehicle);
console.log(v instanceof Car);
console.log(c instanceof Car);
console.log(b instanceof Car);
console.log(v instanceof Boat);
console.log(c instanceof Boat);
console.log(b instanceof Boat);