1. 추상화 (Abstraction)
- 우리가 객체를 만드는 과정은 현실 또는 가상의 존재를 프로그램 내에서 사용할 용도에 맞게 적절하게 설계하는 과정.
- class를 설계하는 것도 추상화에 해당한다
[ user class를 만들 때 ]
user 객체의 속성 - 프로퍼티
user 객체의 행동 - 메소드
- class 이름과 프로퍼티, 메소드 이름을 잘 지어야 한다.
- 이름이 직관적으로 이해하기 쉬워야 다른 개발자들과 협업하기에 좋다.
- 이름을 의미가 있는 단어들로 짓기.
- 프로퍼티와 메소드에 그 의미를 나타내는 간단한 주석을 적어주기.
2. 캡슐화 (Encapsulation)
- 객체의 특정 프로퍼티에 직접 접근하지 못하도록 막는 것. 이를 통해 프로그래밍의 안전성을 높일 수 있다.
- 특정 프로퍼티에 대한 접근을 미리 정해진 메소드들을 통해서만 가능하게 하는 것.
- 캡슐화는 객체 외부에서 함부로 접근하면 안되는 프로퍼티나 메소드에 직접 접근할 수 없도록 한다.
2-1. setter 메소드
- 프로퍼티를 보호한다.
- 프로퍼티의 값을 설정한다.
- setter 메소드를 설정하면 해당 객체를 사용하는 다른 개발자가 프로퍼티에 이상한 값을 설정하는 것을 방지할 수 있다.
- 언더바가 붙은 프로퍼티는 새롭게 설정한 프로퍼티.
숨기고자 하는 프로퍼티 이름 앞에 언더바를 사용한다.
2-2. getter 메소드
- 프로퍼티를 보호한다.
- 프로퍼티의 값을 구하는 함수.
- 프로퍼티의 값을 읽는 용도로 사용하기 때문에 파라미터를 따로 써줄 필요 없다.
2-3. 캡슐화 하는 방법
_email 프로퍼티에 직접 접근하는 방법 대신에, email이라는 getter/setter 메소드로만 접근한다.
class User {
constructor(email, birthdate) {
this.email = email;
this.birthdate = birthdate;
}
buy(item) {
console.log(`${this.email} buys ${item.name}`);
}
// getter
get email() {
return this._email;
}
// setter
set email(address) {
if (address.includes('@')) {
this._email = address;
} else {
throw new Error('invalid email address');
}
}
}
const user1 = new User('doordie@took.com', '1992-09-23');
user1.email = 'newDoordie@took.com';
console.log(user1.email);
- 위 코드는 완벽한 캡슐화가 된 상태가 아니다.
보호하려는 프로퍼티 _email에 아래와 같은 방법으로 접근이 가능하기 때문이다. - 자바스크립트에는 캡슐화를 자체적으로지원하는 문법이 없다.
- 클로저(Clouser)라는 개념으로 우회해서 완벽한 캡슐화를 할 수 있다.
console.log(user1._email);
user1._email = 'tooktak';
2-4. 클로저(Clouser) : 완벽한 캡슐화
어떤 함수와 그 함수가 참조할 수 있는 값들로 이루어진 환경을 하나로 묶은 것.
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;
}
const user1 = createUser('doordie@took.com', '1992-09-23');
console.log(user1.email);
- 위 코드를 보면
1. createUser 함수 안에
2. user 객체 바깥에
_email 이라는 변수가 있다. - user 객체 안에는 _email 변수의 값을 읽고 쓸 수 있는 email 이라는 getter/setter 메소드가 있다.
- 마지막 부분에서 createUser라는 factory functon으로 user1이라는 객체를 생성했다.
user1 객체의 email getter 메소드를 호출했고, 결과는 'doordie@took.com'이다.
즉, 함수 안의 변수의 값을 이미 리턴된 객체에서 읽었다. = 클로저로 인해 가능.
- createUser 함수가 실행되는 시점에 email 이라는 getter/setter 메소드는 _email 이라는 변수의 값에 접근할 수 있다.
핵심은 이 email getter/setter 메소드들은 메소드를 갖고 있는 객체가 리턴된 이후여도 여전히 _email 에 접근이 가능하다.
이렇게 함수가 정의된 당시에 참조할 수 있었던 변수들을 계속해서 참조할 수 있는 상태의 함수를 클로저라고 한다.
2-5. 메소드도 캡슐화 가능
function createUser(email, birthdate) {
const _email = email;
let _point = 0;
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;
}
const item = {
name: '맥북',
price: 10000,
};
const user1 = createUser('doordie@took.com', '1992-09-23');
user1.buy(item);
user1.buy(item);
user1.buy(item);
console.log(user1.point);
- _point 변수 추가. 사용자가 물건을 살 때마다 1포인트씩 적립해줄 목적으로 만든 변수.
- _point 변수를 1씩 늘려주는 함수는 increasePoint 함수.
- point 메소드 정의해둔 상태
- incresePoint 라는 함수는 유저 객체의 buy 메소드 안에서 쓰이고 있다.
- buy 메소드를 실행할 때 그 안에서 increasePoint 함수를 호출.
- 맨 마지막 부분의 코드를 보면 user1 객체의 buy 메소드를 호출하여 point getter 메소드를 호출하고 있다.
// 결과
// doordie@took.com buys 맥북
// doordie@took.com buys 맥북
// doordie@took.com buys 맥북
// 3
- increasePoint 함수는 보호 받고 있기 때문에, user1 객체로 바로 increasePoint 함수를 호출할 수 없다.
- 이런식으로 메소드 혹은 함수도 프로퍼티와 마찬가지로 클로저를 통해 캡슐화를 해서 보호할 수 있다.
const user1 = createUser('doordie@took.com', '1992-09-23');
user1.buy(item);
user1.buy(item);
user1.buy(item);
console.log(user1.point);
user1.increasePoint(); // user1 객체로 increasePoint 직접 호출 => 에러 발생
3. 상속 (Inheritance)
부모 클래스의 프로퍼티와 메소드를 자식 클래스가 그대로 물려받는 것.
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}`);
}
}
- PremiumUser 클래스가 User 클래스에 있는 email, birthdate 프로퍼티와 buy 메소드를 물려 받고 있다.
- 상속을 적용하면 똑같은 코드를 다시 작성하지 않아도 되어서, '코드의 재사용성(reusability)'이 좋아진다.
- 필요한 경우 자식 클래스에서 부모 클래스와 동일한 이름의 메소드를 재정의(오버라이딩 overriding)할 수 있다.
=> '다형성'과 연관. - derived class : 자식 클래스
4. 다형성 (Polymorphism)
하나의 변수가 다양한 종류의 클래스로 만든 여러 객체를 가리킬 수 있음을 의미한다.
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;
}
buy(item) {
console.log(`${this.email} buys ${item.name} with a 5% discount`);
}
streamMusicForFree() {
console.log(`Free music streaming for ${this.email}`);
}
}
const item = {
name: '맥북',
price: 10000,
};
const user1 = new User('chris123@google.com', '19920321');
const user2 = new User('rachel@google.com', '19880516');
const user3 = new User('brian@google.com', '20051125');
const pUser1 = new PremiumUser('niceguy@google.com', '19891207', 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);
});
- forEach문 안의 user는 User 클래스로 만든 객체를 가리킬 때도 있고, PremiumUser 클래스로 만든 객체를 가리킬 때도 있다.
- 매번 user 객체의 buy 메소드가 호출된다는 점은 같지만, 구체적으로 무슨 클래스로 만든 객체의 buy 메소드가 호출되느냐에 따라 결과가 달라진다 => 단순한 코드로 다양한 결과를 낼 수 있는 건 다형성 덕분.
- super : 자식 클래스에서 부모 클래스의 생성자 함수를 호출할 때 / 부모 클래스의 일반 메소드를 호출할 때 사용.
instanceof 연산자
현재 변수가 가리키는 객체가 정확히 어느 클래스로 만든 객체인지 확인하는 법.
1.
const user1 = new User('chris123@google.com', '19920321');
const user2 = new User('rachel@google.com', '19880516');
const user3 = new User('brian@google.com', '20051125');
const pUser1 = new PremiumUser('niceguy@google.com', '19891207', 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) => {
console.log(user instanceof PremiumUser);
});
// 결과
// false
// true
// false
// true
// false
// true
2. PremiumUser가 User 클래스를 상속했기 때문에 instanceof User 하면 모두 true 결과값이 나온다.
자식 클래스로 만든 객체는 부모 클래스로 만든 객체로도 인정이 된다.
const user1 = new User('chris123@google.com', '19920321');
const user2 = new User('rachel@google.com', '19880516');
const user3 = new User('brian@google.com', '20051125');
const pUser1 = new PremiumUser('niceguy@google.com', '19891207', 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) => {
console.log(user instanceof User);
});
// 결과
// true
// true
// true
// true
// true
// true
클래스는 파일 하나당 하나씩 만들 것 ★★★★★
- 한 눈에 코드를 보기 위해 해당 게시물들의 코드들은 하나의 코드블럭 작성되었다.
- export와 import를 사용하여 파일별로 클래스와 메인 로직을 쪼개서 작성하는 것이 좋다.
728x90
'코딩도 합니다 > JS' 카테고리의 다른 글
[자바스크립트 js] Fetch / request / response / 콜백 (0) | 2021.06.27 |
---|---|
[자바스크립트 js] static 프로퍼티 / static 메소드 (0) | 2021.06.27 |
[자바스크립트 js] 객체 만들기 / Object-Literal / Factory function / Constructor function (0) | 2021.06.19 |
[자바스크립트 js] 객체 지향 프로그래밍이란 (0) | 2021.06.19 |
[자바스크립트 js] 모듈 복습 및 활용 / 직전 게시물 정리 (0) | 2021.06.19 |