객체 지향 프로그래밍, 추상화, 캡슐화, 상속, 다형성
객체 지향 프로그래밍이란?
객체 만들기 1-1: Object-Literal
Object-Literal
중괄호를 쓰고 그 안에 프로퍼티와 메소드를 나열하는 것const user = {
email: 'chris123@google.com',
birthdate: '1992-03-21',
buy(item) {
console.log(`${this.email} buys ${item.name}`);
},
};
객체 만들기 1-2: Factory function
factory function
객체를 생성해서 리턴하는 함수
function createUser(email, birthdate) {
const user = {
email,
birthdate,
buy(item) {
console.log(`${this.email} buys ${item.name}`);
},
};
return user;
}
const user1 = createUser('a@email.com', '1999-09-01');
const user2 = createUser('b@email.com', '1998-07-02');
const user3 = createUser('c@email.com', '1990-11-24');
객체 만들기 2: Constructor function
Constructor Function
함수 안에서 this를 활용해 객체를 생성하고 리턴하는 특별한 함수
this
키워드는 자바스크립트의 다양한 상황에서 다르게 쓰이며, constructor function 내에서는 생성할 객체를 가리킨다.
new 키워드를 앞에 붙여서 함수를 호출해야 정상적으로 작동한다.
리턴문을 작성하지 않아도 생성한 객체를 자동으로 리턴한다.
함수의 첫 문자를 대문자로 지정하도록 많은 스타일가이드에서 권장한다.
function User(email, birthdate) {
this.email = email;
this.birthdate = birthdate;
this.buy = function (item) {
console.log(`${this.email} buys ${item.name}`);
};
}
const user1 = new User('a@email.com', '1999-09-01');
const user2 = new User('b@email.com', '1998-07-02');
const user3 = new User('c@email.com', '1990-11-24');
객체 만들기 3: Class(ES2015)
Class
객체를 생성하기 위한 템플릿
this
키워드는 Class 내에서 생성할 객체를 가리킨다.
보통 프로퍼티는 constructor()
안쪽에, 메소드나 getter는 constructor()
바깥쪽에 작성한다.
Declaration, unnamed expression, named expression 세 가지 방법으로 Class 객체를 만들 수 있다.
Hoisting을 허용하지 않는다.
// Class 선언식(declaration)
class User1 {
constructor(email, birthdate) {
this.email = email;
this.birthdate = birthdate;
}
buy(item) {
console.log(`${this.email} buys ${item.name}`);
}
}
const user1 = new User('a@email.com', '1999-09-01');
// Class 표현식(expression)
// unnamed
const User = class {
constructor(email, birthdate) {
this.email = email;
this.birthdate = birthdate;
}
buy(item) {
console.log(`${this.email} buys ${item.name}`);
}
}
const user1 = new User('a@email.com', '1999-09-01');
// Class 표현식(expression)
// named
const User = class UserClassName {
constructor(email, birthdate) {
this.email = email;
this.birthdate = birthdate;
}
buy(item) {
console.log(`${this.email} buys ${item.name}`);
}
}
const user1 = new User('a@email.com', '1999-09-01');
User.name // "UserClassName"
추상화(abstraction): 어떤 구체적인 것을 원하는 방향으로 간략화해서 나타내는 것
캡슐화(Encapsulation): 객체의 특정 프로퍼티에 직접 접근하지 못하도록 막는 것
특정 프로퍼티에 대한 접근을 getter, setter 메소드들을 통해서만 가능하도록 할 수 있다
class User {
constructor(email, birthdate) {
this.email = email;
this.birthdate = birthdate;
}
buy(item) {
console.log(`${this.email} buys ${item.name}`);
}
get email() {
return this._email;
}
set email(address) {
if (addrress.includes('@')) {
this._email = address;
} else {
throw new Error('invalid email address');
}
}
}
get()
getter 메소드는 obj.propName을 사용해 프로퍼티를 읽으려고 할 때 실행된다.
set()
setter 메소드는 obj.propName = value을 사용해 프로퍼티에 값을 할당하려 할 때 실행된다.
프로퍼티 네임과 동일한 이름을 getter, setter의 이름으로 사용했기 때문에, 프로퍼티에 접근하려면 obj._propName
처럼 프로퍼티 네임 앞에 언더바(_)를 붙여서 접근할 수 있다.
getter, setter만 설정할 경우 obj._propName
의 값은 여전히 접근 가능하고, 수정도 가능하기 때문에 완벽한 캡슐화라고 볼 수 없다.
캡슐화 더 알아보기
완벽한 캡슐화
Java에는 private이라는 키워드가 있어서 문법 차원에서 캡슐화를 지원하지만, JavaScript에는 캡슐화를 자체적으로 지원하는 문법이 아직 없다.
하지만 JavaScript에서도 클로저(Closure)라고 하는 개념을 응용해서 완벽한 캡슐화를 할 수 있다
closure
어떤 함수와 그 함수가 참조할 수 있는 값들로 이루어진 환경을 하나로 묶어서, 함수가 정의된 당시에 참조할 수 있었던 변수들을 계속 참조할 수 있는 상태의 함수
function createUser(email, birthdate) {
let _email = email;
const user = {
birthdate,
get email() {
return _email;
},
set email(address) {
if (address.includes('@')) {
_email = address;
} else {
throw new Error('invalid email address');
}
},
};
return user;
}
function 안, user 객체 바깥에 _email 변수를 만들고, _email 변수의 값을 읽고 쓸 수 있는 email이라는 getter/setter 메소드를 만들면 완벽한 캡슐화가 가능하다.
메소드에도 closure
개념을 응용할 수 있다
function createUser(email, birthdate) {
const _email = email;
let _point = 0;
// 사용자가 물건을 살 때마다 1포인트씩 적립해 주는 함수
function increasePoint() {
_point += 1;
}
const user = {
birthdate,
get email() {
return _email;
},
get point() {
return _point;
},
buy(item) {
console.log(`${this.email} buys ${item.name}`);
increasePoint();
},
};
return user;
}
function 안, user 객체 바깥에 _point 변수와 incresePoint() 함수를 만들고, _point 변수의 값을 읽을 수 있는 point라는 getter 메소드를 만들면 완벽한 캡슐화가 가능하다.
상속(inheritance): 자식 객체가 부모 객체의 프로퍼티와 메소드를 물려받는 것
extends
클래스를 다른 클래스의 자식으로 만들기 위해 사용되는 키워드
class User {
constructor(email, birthdate) {
this.email = email;
this.birthdate = birthdate;
}
buy(item) {
console.log(`${this.email} buys ${item.name}`);
}
}
class PremiumUser extends User {
constructor(email, birthdate, level) {
super(email, birthdate);
this.level = level;
}
streamMusicForFree() {
console.log(`Free music streaming for ${this.email}`);
}
}
자식 클래스의 constructor()
안쪽에 super()
키워드를 호출해야 자식 클래스에서 this 키워드에 접근할 수 있다. 호출하기 전에는 ReferenceError가 발생한다.
다형성(Polymorphism): 프로그램 언어 각 요소들이 다양한 데이터 타입에 속하는 것이 허가되는 성질
const user1 = new User('chris123@google.com', '19920321');
const user2 = new User('rache@google.com', '19880516');
const user3 = new User('brian@google.com', '20051125');
const pUser1 = new PremiumUser('tommy@google.com', '19901207', 3);
const pUser2 = new PremiumUser('helloMike@google.com', '19900915', 2);
const pUser3 = new PremiumUser('alicekim@google.com', '20010722', 5);
const users = [user1, pUser1, user2, pUser2, user3, pUser3];
users.forEach((user) => {
user.buy(item);
});
user
가 다양한 종류의 객체 User
, PremiumUser
를 가리킬 수 있는 것도 다형성
이라고 한다.부모 클래스의 메소드가 필요하다면?
super.method()
키워드를 사용해서 부모 클래스의 메소드를 자식 클래스 내에서 불러올 수 있다.instanceof 연산자
object instanceof constructor
constructor의 프로토타입 프로퍼티가 객체의 프로토타입 체인에 나타나는지 여부를 검사하는 연산자static 프로퍼티와 static 메소드
static
클래스로 만든 객체가 아니라 클래스 자체로 접근할 수 있는 프로퍼티나 메소드
class Math {
static PI = 3.14;
static getCircleArea(radius) {
return Math.PI * radius * radius;
}
}
// static 프로퍼티 값 변경
Math.PI = 3.141592;
// static 메소드 추가
Math.getRectangleArea = function (width, height) {
return width * height;
}
console.log(Math.PI);
console.log(Math.getRectangleArea(4, 5));
// Date 내장 객체의 static 메소드 now()
console.log(Date.now());
static property나 method도 getter, setter 메소드로 캡슐화할 수 있다.
클래스는 파일 하나당 하나씩 넣어주는 게 좋아요
Reference
- Object-oriented programming - Wikipedia
- Process-oriented programming - Wikipedia
- The JavaScript “this” Keyword Deep Dive: Constructor Functions | Kevin Chisholm - Blog
- Classes - JavaScript | MDN
- extends - JavaScript | MDN
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof
- 자바스크립트 객체 지향 기본기 - 자바스크립트 강의 | 코드잇