Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions 7_클래스/최재훈.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# 코어 자바스크립트 Note

## 07. 클래스

자바스크립트는 프로토타입 기반 언어이기에 '상속'이라는 개념이 존재하지 않는다.

클래스 기반의 다른 언어에 익숙한 개발자들에겐 혼란,

그래서, 클래스와 비슷하게 동작하게끔 흉내 내는 여러 기법들이 탄생했다.

ES6에는 클래스 문법이 추가되었지만 일정 부분은 프로토타입을 활용

즉, ES5 체제 하에서 클래스를 흉내내기 위한 구현 방식을 학습.

### 01. 클래스와 인스턴스의 개념 이해

- 어떤 클래스의 속성을 지니는, 실존하는 개체를 일컬어 인스턴스(instance)라고 한다.

- 어떤 조건에 부합하는 구체적인 예시, 조건은 곧 클래스를 의미, 어떤 클래스에 속한 개체는 그 클래스의 조건을 모두 만족하므로 그 클래스의 구체적인 예시, 즉 인스턴스가 될 것이다.

- 현실세계에서는 개체들이 이미 존재하는 상태에서 이들을 구분짓기 위해 클래스를 도입한다.

- 이 때문에 하나의 개체가 같은 레벨에 있는 서로 다른 여러 클래스의 인스턴스일 수 있다.
- 예를 들어, 필자는 남성이면서 회사원이고 한국인이라는 여러 분류에 속하고
- 남성, 회사원, 한국인의 각 분류는 서로 밀접한 관련이 없는 별개의 분류체제

- 반면 프로그래밍 언어상에서는 접근 방식이 정반대임!
- 컴퓨터는 위와 같은 구분법을 알지 못하므로, 사용자가 직접 여러 가지 클래스를 정의해야 하고, 클래스를 바탕으로 인스턴스를 만들 때 비로서 어떤 개체가 클래스의 속성을 지니게 된다.
- 또한 한 인스턴스 하나의 클래스만을 바탕으로 만들어진다
- 어떤 인스턴스가 다양한 클래스에 속할 수는 있지만 이 클래스들은 모두 인스턴스 입장에서는 '직계존속'이다.
- 다중상속을 지원하는 언어든 그렇지 않은 언어든 결국 인스턴스를 생성할 때 호출할 수 있는 클래스는 오직 하나뿐일 수밖에 없기 때문이다.
- 클래스는 '공통 요소를 지니는 집단을 분류하기 위한 개념'이라는 측면에서는 일치하지만 인스턴스들로부터 공통점을 발견해서 클래스를 정의하는 현실과 달리, 클래스가 먼저 정의돼야만 그로부터 공통적인 요소를 지니는 개체들을 생성할 수 있다.
- 현실세계에서의 클래스는 추상적인 개념이지만, 프로그래밍 언어에서의 클래스는 사용하기에 따라 추상적일 수도 있고 구체적인 개체가 될 수도 있다.

### 02. 자바스크립트의 클래스

- 생성자 함수 Array를 new 연산자와 함께 호출하면 인스턴스가 생성된다.

- Array를 일종의 class라고 하면, Array의 prototype 객체 내부 요소들이 인스턴스에 '상속'된다.
- 엄밀히는 상속이 아닌 프로토타입 체이닝에 의한 '참조'지만, 결과적으론 동일하게 동작.
- Array 내부 프로퍼티들 중 prototype 프로퍼티를 제외한 나머지는 인스턴스에 상속되지 않음.

- 인스턴스에 상속되는지 여부에 따라 스태틱 멤버와 인스턴스 멤버로 나뉨.
- '인스턴스 메서드'라는 명칭은 프로토타입에 정의한 메서드를 지칭하는 것인지 인스턴스에 정의한 메서드를 지칭하는 것인지 혼란.
- 즉, 프로토타입 메서드라고 부르자!

```js
// 생성자
var Rectangle = function (width, height) {
this.width = width;
this.height = height;
};
// (프로토타입) 메서드
Rectangle.prototype.getArea = function () {
return this.width + this.height;
};
// 스태틱 메서드
Rectangle.isRectangle = function (instance) {
return (
instance instanceof Rectange && instance.width > 0 && instance.height > 0
);
};

var rect1 = new Rectangle(3, 4);
console.log(rect1.getArea()); // 12 (0)
console.log(rect1.isRectangle(rect1)); // Error(X)
console.log(Rectangle.isRectangle(rect1)); // true
```

- rect1 인스턴스에서 isRectangle이라는 메서드에 접근해보자.
- rect1에 해당 메서드가 있는지 검색했는데 없고, rect1.**proto**에도 없고, 그 뒤에도 없다.
- 결국, undefined를 실행하라는 명령이므로 typeError를 띄운다.
- 이렇게 인스턴스에서 직접 접근할 수 없는 메서드를 '스태틱 메서드'라고 한다.
- 이것은 생성자 함수를 this로 해야만 호출할 수 있다. 아래의 Rectangle.isRectangle(rect1)처럼.

### 03. 클래스 상속

자바스크립트에서 클래스 상속을 구현했다는 것은 결국 프로토타입 체이닝을 잘 연결한 것.

#### 7-3-1. 기본 구현

- 프로토타입 체인을 활용해 클래스 상속을 구현하고 최대한 전통적인 객체지향 언어에서의 클래스와 비슷한 형태로까지 발전
- ES5까지의 자바스크립트엣는 클래스가 없다. ES6에서 클래스가 도입됐지만 역시나 prototype을 기반으로 한 것으로 기본적으로는 결국 프로토타입 체이닝을 잘 연결한 것으로 이해한다.

- 다만 '기본적으로' 그렇다는 것이고, 세부적으로 완벽하게 superclass와 subclass의 구현이 이루어진 것은 아니다.
- Grade 예제에서 length 프로퍼티가 configurable(삭제 가능)하다는 점과, Grade.protoype에 빈 배열을 참조시켰다는 점이 그렇다.

```js
g.push(90);
console.log(g): // Grade {0: 100, 1: 80, 2: 90, legnth : 3}

delete g.length;
g.push(70);
console.log(g); // Grade { 0: 70, 1:80, 2:90, length:1}
```

- legnth 프로퍼티를 삭제하고 다시 push를 했떠니, push한 값이 0번쨰 인덱스에 들어갔고, legnth가 1이 됐다.

- 내장객체인 배열 인스턴스의 length 프로퍼티는 configurable 속성이 false라서 삭제가 불가능하지만, Grade 클래스의 인스턴스는 배열 메서드를 상속하지만 기본적으로는 일반 객체의 성지를 그대로 지니므로 삭제가 가능해서 문제가 된다.
- 저렇게 된 이유는?
- 바로 g.**proto **, 즉 Grade.prototype이 빈 배열을 가리키고 있기 떄문이다.
- push 명령에 의해 엔진이 g.legnth를 읽고자 하는데 g.length가 없으므로 프로토타입 체이닝을 타고 g.**proto **.length를 읽어온 것
- 빈 배열의 legnth가 0이므로 여기에 값을 할당하고 legnth는 1만큼 증가시키라는 명령이 문제 없이 동작할 수 있었던 이유!
- 그럼 만약 Grade.prototype에 요소를 포함하는 배열을 매칭시켰다면?

```js
Grade.prototype = ['a', 'b', 'c', 'd'];
var g= new Grade(100, 80);

g.push(90);
conols.log(g); / Grade {0: 100, 1: 80, 2:90, legnth:3}

delete g.length;
g.push(70);
console.log(g); // Grade {0:100, 1: 80, 2: 90, __ 4:70, legnth:5}
```

- g.length가 없으니 g.**proto **.legnth를 찾고, 값이 4이므로 인덱스 4에 70을 넣고, 다시 g.length에 5를 부여하는 순서로 동작한 것
- 이처럼 클래스에 있는 값이 인스턴스의 동작에 영향을 주면 안된다!
- 이런 영향을 줄 수 있다는 것 자체가 이미 클래스의 추상성을 해치는 것이다.

#### 7-3-2. 구체적인 데이터를 지니지 않게 하는 방법

다양한 방식이 존재하지만

1. 인스턴스 생성 후 프로퍼티 제거하는 방법

2. 빈 함수를 이용하는 방법

3. Object.create를 활용하는 방법

같은 접근 방식이 있다.

결국, SubClass.prototype의 **proto**가 SuperClass.prototype를 참조하고
SubClass.prototype에는 불필요한 인스턴스 프로퍼티가 남아있지 않게 된다.

#### 7-3-3. consturctor 복구하기

프로토타입 체인상에 가장 먼저 등장하는 SuperClass.prototype의 constructor에서 가리키는 대상, 즉 SuperClass.

### 07-5. 정리

- 자바스크립트는 프로토타입 기반 언어라서 클래스 및 상속 개념은 존재하지 않지만 프로토타입을 기반으로 클래스와 비슷하게 동작하게끔 하는 다양한 기법들이 도입됨!
- 클래스는 어떤 사물의 공통 속성을 모아 정의한 추상적인 개념이고, 인스턴스는 클래스의 속성을 지니는 구체적인 사례!
- 상위 클래스의 조건을 충족하면서 더욱 구체적인 조건이 추가된 것을 하위 클래스라고 한다.

- 클래스의 prototype 내부에 정의된 메서드를 프로토타입 메서드라고 하며,

- 이들은 인스턴스가 마치 자신의 것처럼 호출할 수 있다.

- 한편 클래스(생성자 함수)에 직접 정의한 메서드를 스태틱 메서드라고 하며, 이들은 인스터스가 직접 호출할 수 없고 클래스(생성자 함수)에 의해서만 호출할 수 있다.

- 클래스 상속을 흉내내기 위한 3가지 방법

- SubClass.prototype에 SuperClass의 인스턴스를 할당한 다음 프로퍼티를 모두 삭제하는 방법, 빈 함수(Bridege)를 활용하는 방법, Object.create를 이용하는 방법.
- 세 방법 모두 constructor 프로퍼티가 원래의 생성자 함수를 바라보도록 조정해야 한다.

- 상위 클래스에 접근할 수 있는 수단인 super를 구현함