let counter = (function() {
let privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
};
})();
console.log(counter.value()); // 0
counter.increment();
counter.increment();
console.log(counter.value()); // 2
counter.decrement();
console.log(counter.value()); // 1
let makeCounter = function() {
let privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
let counter1 = makeCounter();
let counter2 = makeCounter();
console.log(counter1.value()); // 0
counter1.increment();
counter1.increment();
console.log(counter1.value()); // 2
counter1.decrement();
console.log(counter1.value()); // 1
console.log(counter2.value()); // 0
객체 지향 프로그래밍이란?
하나의 모델이 되는 청사진(설계도)을 만들고(class), 이를 바탕으로 한 객체(object)를 만드는(instance) 프로그래밍 패턴
클래스에 속성과 메소드 정의, 인스턴스에서 이용
클래스: 속성의 정의
클래스: 메서드의 정의
function Student(name, age, gender){} // 속성 정의
Student.prototype.study = function() {} // 메서드 정의
class Student {
constructor(name, age, gender){} // 속성 정의
study(){} // 메서드 정의
}
class Car {
constructor(brand, name, color){
this.brand = brand;
this.name = name;
this.color = color;
}
drive() {
console.log(this.name + '가 운전을 시작합니다');
}
} // ES6 방식으로 작성해 본 Car 클래스
- 프로그램 설계 철학 중 하나
- 객체(속성과 메서드가 존재)로 그룹화
- 객체 생성 시 메모리 상에서 반환되기 전까지 객체(속성, 메서드) 유지
- 4가지 주요 개념을 통해 재사용성 획득
- 캡슐화, 상속, 추상화, 다형성
- Encapsulation(캡슐화)
- 데이터와 기능(속성과 메서드)을 하나의 객체 안에 넣어 묶는 것
- 느슨한 결합(Loose Coupling)에 유리: 원할 때 구현 수정 가능
- 캡슐화는 “은닉화”의 특징도 포함
- 은닉화: 내부 데이터나 내부 구현이 외부로 노출되지 않도록 하는 것
-> 데이터나 구현은 숨기고, 메서드(동작)만 노출시킴- Abstraction(추상화)
- 복잡한 내부 구현과 달리 인터페이스 단순화를 구현하는 것
- 예기치 못한 변화의 영향을 줄임
- 캡슐화는 데이터의 ‘은닉’에 focusing, 상속은 클래스를 ‘단순’한 이름으로 정의하는 것에 focusing
- 인터페이스: 클래스 정의 시, 메서드와 속성만 정의한 것(추상화의 본질)
- Inheritance(상속)
- 기본 클래스(부모)의 특징을 파생 클래스(자식)가 물려받는 것
- 즉 부모 클래스의 속성이나 메서드를 자식 클래스가 그대로 물려받아 사용 가능(속성이나 메서드 추가 가능)
- Polymorphism(다형성)
- 다양한 형태를 가지는 것 -> 단일 작업을 다른 형태로 수행하는 것
- ex) 같은 이름의 메서드가 조금씩 다르게 작동
프로토타입이란
- 모델의 청사진(클래스)을 만들 때 쓰는 원형 객체
- JavaScript는 프로토타입 기반 언어, DOM도 프로토타입으로 상속 구현
- 보통 클래스의 인스턴스는 new 키워드로 생성, DOM은 new 키워드가 아닌 createElement 사용
__proto__
속성prototype
속성__proto__
(프로토타입 체인에 의해 생략)가 생성자의 prototype
속성을 참조 -> person1.__proto__
.greeting() 이 아닌 person1.greeting() 으로 prototype에 있는 메소드에 접근 가능프로토타입 체인
각각의 객체는 [[prototype]]이라는 private(은닉) 속성을 가지는데 이 속성은 자신의 프로토타입이 되는 다른 객체를 가리키고 그 객체는 그 객체의 프로토타입의 프로토타입을 가리키며 이 과정을 반복하며 null을 프로토타입으로 가지는 객체에서 끝나며 프로토타입 체인의 종점 역할을 함
어떤 객체 안에 원하는 속성이 있으면 그 객체 안의 속성 값을 리턴하고 원하는 프로퍼티가 없으면 생성자를 찾아 그 객체의 prototype 객체를 확인하여 그 객체의 프로퍼티 중 원하는 프로퍼티의 값이 있으면 그것을 반환, 없으면 또 생성자 객체의 생성자를 찾아 그 객체의 prototype 객체를 확인하여 원하는 프로퍼티 값을 찾는다.(있을 때까지 찾아 올라감 -> chain 사용)
//Human 클래스 생성
class Human {
constructor(name, age) {
this.name = name;
this.age = age;
}
greeting() {
console.log(`Hi! I'm ${this.name}.`);
}
}
// Author 클래스는 Human 클래스로부터 상속받음(extends 키워드 사용)
// 부모 클래스의 생성자 참조(super 키워드 사용)
class Author extends Human {
constructor(name, age, bookname, publisher){
super(name, age);
this.bookname = bookname;
this.publisher = publisher;
}
}
let author1 = new Author('Hermann Hesse', 120, 'demian', 'Harper Perennial'); // Author 클래스의 새로운 인스턴스를 변수 author1에 할당
author1.greeting(); // 부모 클래스의 메서드 접근 가능
author1.name; // 부모 클래스의 속성 접근 가능
author1.bookname; // 자식 클래스에서 추가한 속성 접근 가능
super
키워드
this
키워드가 사용되기 전에 호출되어야 함extends
키워드
class
자식 클래스 extends
부모 클래스ES6 문법으로 작성한 클래스 상속 코드를 ES5 문법으로 작성해보기
상황: Student 클래스가 Human 클래스를 상속 받는 경우
// ES5 문법으로 작성한 클래스 상속
function Human (name, age) {
this.name = name;
this.age = age;
}
Human.prototype.sleep = function () {
return `${this.name}이(가) 잠을 잡니다.`
}
function Student (name, age, grade) {
Human.call(this, name, age); // Human 생성자함수를 call로 호출하여 name과 age를 가져옴
this.grade = grade;
}
Student.prototype = Object.create(Human.prototype); // 부모 클래스의 함수를 가져옴
Student.prototype.constructor = Student; // create 함수를 통해 Human으로 바뀐 생성자함수를 Student로 변경, 그렇지 않을 경우 하위 클래스의 변경이 상위 클래스의 변경에도 영향을 미침
Student.prototype.study = function (num) {
this.grade += num;
return `${this.name}의 성적이 ${num}만큼 올라 ${this.grade}이 되었습니다.`;
}
let mincoding = new Student('민학생', 19, 70); // Student 클래스의 인스턴스 생성
mincoding.study(10); //'민학생의 성적이 10만큼 올라 80이 되었습니다.' 출력
- 기본적으로 Javascript에는
interface
키워드가 없음- 인터페이스는 구현해야 하는 속성과 메서드만 설명, 메서드가 작동하는 방식은 설명하지 않음
-> 이와 같이 인터페이스 단순화를 통해 추상화 구현- 인터페이스는 직접 인스턴스 생성 불가
- TypeScript에서의 인터페이스 구현
interface
키워드로 interface를 정의- 클래스 선언문 뒤에 implements 키워드로 인터페이스 선언
- 이 경우 해당 클래스는 지정된 인터페이스(인터페이스에서 정의한 속성과 메서드)를 반드시 구현해야 함
typescript 인터페이스
#
으로 은닉화 구현해보기
- 기본적으로 JavaScript의 class 속성은 기본적으로 public 하며 클래스 외부에서 접근, 수정 가능
ES2019부터#
을 추가해 private 클래스 필드를 선언 가능
- scope 밖에서 캡슐화한 속성에 접근 불가능
// Human 클래스 내부 name 속성 은닉화 구현
class Human {
#name
constructor(age){
this.#name = 'goo';
this.age = age;
}
sleep(){ return `${this.#name}이(가) 잠을 잡니다.`};
}
// 새로운 인스턴스 생성
let person1 = new Human(16);
person1.age; // 결과값 16, 접근 가능
person1.name; // 결과값 undefined, 접근 불가능(은닉화 구현)
person1.sleep(); // 결과값 'goo이(가) 잠을 잡니다.', 접근 가능
생성한 클래스 인스턴스의 속성 값을 변경하거나 최종 값을 예측할 수 없는 경우 사용
- getter와 setter는 쌍으로 동작
get
과set
이용
// Student 클래스의 grade 속성을 변경하고 싶은 상황 가정
// Human 클래스
class Human {
constructor(name, age){
this.name = name;
this.age = age;
}
sleep(){ return `${this.name}이(가) 잠을 잡니다.`}
}
// Human 클래스를 상속받은 Student 클래스
class Student extends Human{
constructor(name, age, grade){
super(name, age);
this._grade = grade;
}
study(num){
this._grade = this._grade + num;
return `${this.name}의 성적이 ${num}만큼 올라 ${this._grade}이 되었습니다.`
}
// getter
get grade() {
return this._grade;
}
// setter
set grade(newGrade) {
this._grade = newGrade;
}
}
// 새로운 인스턴스 생성
let mincoding = new Student('민학생', 19, 70);
mincoding._grade; // 결과값은 70
mincoding._grade = 90; // 인스턴스의 속성 값 변경
mincoding.study(10); // '민학생의 성적이 10만큼 올라 100이 되었습니다.' 출력
// 즉, getter와 setter를 이용해 인스턴스의 속성 값 변경 가능
<오늘의 일기>
이해하기 만만치 않았던 unit2. 특히 프로토타입 속성이 나에게는 굉장히 추상적으로 다가와 이해하기 쉽지 않았고 아직까지도 남들에게 설명할 수 있을 정도로 완벽하게 이해하지는 못했지만 그래도__proto__
가 무엇을 가리키며prototype
은 무엇을 가리키는지 정도는 이해한 것 같다.
이번 유닛에서는 class상속에 대한 과제를 수행했는데 이번 페어분께서는 굉장히 꼼꼼하게 의사코드를 작성하시는 모습을 보고 그 점을 배워야겠다는 생각이 들었다.
쉽지 않은 여정임은 분명하지만 하나하나 완벽하게 이해하기보다는 틀을 잡고 그 위에 계속해서 살을 덧붙여 가다보면 언젠가는 어떠한 형태로 완성될 수 있을 것이라 생각하며 계속해서 공부해나가야겠다.