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
279 changes: 278 additions & 1 deletion 7장/7장_창헌.md
Original file line number Diff line number Diff line change
@@ -1 +1,278 @@
### 벌써 7장이네요! 마지막까지 화이팅!😤
# Class

## 클래스와 인스턴스

> 클래스: 공통요소를 지니는 집단을 분류하기 위한 개념
> 인스턴스: 어떤 클래스의 속성을 지니는 실존하는 개체

현실: 인스턴스의 공통점으로 클래스 생성
프로그래밍: 클래스를 바탕으로 인스턴스 생성

한 인스턴스는 하나의 클래스만을 바탕으로 만들어진다. 어떤 인스턴스가 다양한 클래스에 속할 수는 있지만 결국 인스턴스를 생성할 때 호출할 수 있는 클래스는 하나이다.

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

생성자 함수 Array + new 연산자 => 인스턴스 생성
이때 Array를 일종의 클래스라고 하면 Array의 prototype 객체 내부 요소들이 인스턴스에 상속된다고 볼 수 있다.( 프로토타입 체이닝에 의한 참조지만 동일하게 동작 )
하지만 Array 내부 프로퍼티 중에서 prototype 프로퍼티를 제외한 나머지는 인스턴스에 상속되지 않는다.

인스턴스에 상속되는지에 따라 스태틱 멤버와 프로토타입 메서드로 나뉜다.

- 스태틱 멤버: 인스턴스에서 직접 접근할 수 없는 메서드, 생성자 함수를 this로 해야만 호출

- 프로토타입 메서드(인스턴스 메서드): 인스턴스에서 직접 호출할 수 있는 메서드

```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 Rectangle && instance.width > 0 && instance.height > 0
);
};
```

일반적인 사용방식 => 구체적인 인스턴스가 사용할 메서드를 정의한 틀의 역할=> 추상적
클래스 자체를 this로 해서 직접 접근해야하는 스태틱 메서드를 호출할 때의 클래스=> 그 자체가 하나의 개체

## 클래스 상속

프로토타입 체이닝을 잘 연결하여 구현 but 문제점 존재

```js
var Grade = function () {
var args = Array.prototype.slice.call(arguments);
for (var i = 0; i < args.length; i++) {
this[i] = args[i];
}
this.length = args.length;
};
Grade.prototype = []; //문제점 2
var g = new Grade(100, 80);
```

### 문제점

1. 프로퍼티를 삭제할 수도 있다.

length라는 프로퍼티를 삭제할 수 있다. 이는 인스턴스가 기본적으로 배열의 메서드를 상속받지만 기본적으로는 일반 객체의 성질을 그대로 지니므로 삭제가 가능해서 문제가 된다.

g.length를 삭제=> g.length 부름=> 삭제해서 없음=> `g.__proto__`까지 가서 length가져옴

2. Grade.prototype에 빈 배열을 참조

만약에 값이 존재했다면 `Grade.__proto__.length` 가 달라져 다르게 동작하게 된다.

=> 클래스에 있는 값이 인스턴스의 동작에 영향을 주면 안됨.

### 여전히 남은 문제

1. 클래스에 구체적인 데이터를 가지고 있어 인스턴스의 값을 지우면 프로토타입 체이닝으로 잘못된 결과 나옴.
2. .constructor가 다른 곳을 바라보고있음(프로토타입 체이닝을따라서)

이처럼 하위 클래스로 삼을 생성자 함수의 prototype에 상위 클래스의 인스턴스를 부여하는 것만으로도 기본적인 메서드 상속은 가능하지만 다양한 문제가 발생할 여지가 있어 구조적으로 안정성이 떨어진다.

### 클래스가 구체적인 데이터를 지니지 않게 하는 방법

1. 일단 만들고 프로퍼티들을 일일이 지우고, 새로 추가 못하게 하기(freeze로 동결)

2. SubClass의 prototype에 직접 SuperClass의 인스턴스를 할당하는 대신 아무런 프로퍼티를 생성하지 않고, 생성자 함수를 하나 더 만들어 그 prototype이 SuperClass의 prototype을 바라보게끔 한 다음 SubClass의 prototype에는 Bridge의 인스턴스를 할당(빈 함수 활용)

3. Object.create활용

=> 결국 SubClass.prototype의 `__proto__`가 SuperClass.prototype을 참고하고, SubClass.prototype에는 불필요한 인스턴스 프로퍼티가 남아있지 않게 한다.

### Constructor 복구

기본적인 상속에는 성공하지만 SubClass 인스턴스의 constructor가 여전히 Super Class를 가리키는 상태이다.

SubClass의 constructor가 존재하지 않고, prototype에도 존재 하지 않는다. 따라서 체인상 가장 먼저있는 SuperClass의 constructor가 출력된다.

SubClass.prototype.constructor가 원래의 SubClass를 바라보도록 해주면 된다.

```js
SubClass.prototype.constructor = SubClass;
```

### 상위 클래스에의 접근수단 제공

하위 클래스의 메서드에서 상위 클래스의 메서드 실행 결과를 바탕으로 추가적인 작업을 수행하고 싶을 때가 있다. 이때 매번 `SuperClass.prototype.method.apply(this,arg)` 로 접근하는 것은 불편하다.

하위클래스에서 상위 클래스의 프로토타입 메서드에 접근할 방법을 제공 => Super

```js
SubClass.prototype.super = function (propName) {
var self = this;
if (!propName)
return function () {
SuperClass.apply(self, arguments);
};
var prop = superClass.prototype[propName];
if (typeof prop !== "function") return prop;
return function () {
return prop.apply(self, arguments);
};
};
```

인자가 비었을 경우 SuperClass 생성자함수에 접근하는 것으로 간주
this가 달라지지 않게 클로저 사용
prototype 내부의 propName에 해당하는 값이 함수가 아니면 해당 값을 그대로 반환, 함수인 경우 클로저를 활용해 메서드에 접근하는 것으로 여김

SuperClass의 생성자 함수에 접근하고자 할때 this.super(),
SuperClass의 프로토타입 메서드에 접근하려면 this.super(propName)으로 사용

<!-- ## ES6에서의 클래스 및 클래스 상속 -->

---

## 2-depth

Javascript는 class를 특별한 타입의 function으로 취급한다.
클래스와 함수의 가장 큰 차이점은 호이스팅이다. 함수와 다르게 Javascript 클래스는 접근하기 전에 선언되어져야 한다.
이미 존재하는 프로토타입 상속을 대신해 줄 class라는 키워드를 제공한다.

### Constructor method

Constructor 메서드는 Javascript의 클래스 객체를 초기화시켜주는 특별한 메서드이다.
Javascript에서 클래스가 선언되면 자동으로 불려진다.
하나의 클래스에 하나의 생성자만 있을 수 있다. 정의되지 않은 경우 JavaScript는 해당 클래스에 매개변수가 0인 빈 생성자를 추가합니다.

### Static method

정적 메서드는 클래스의 인스턴스가 아니라 클래스 자체에서 호출되는 메서드이다. 정적 메서드는 클래스의 인스턴스가 아니지만 기능면에서 클래스와 관련이 있다.

```js
class Math {
static add(number1, number2) {
return number1 + number2;
}
}

let additionResult = Math.add(2, 3);
```

### ES6 Class의 문제점

클래스는 JavaScript에서 특정 작업을 수행하는 간단한 방법에 대한 보다 복잡한 솔루션을 제공할 수 있다. 객체 지향 프로그래밍 언어에 대한 배경 지식이 있는 사람들은 필요하지 않은 경우에도 클래스로 간단한 작업을 수행하기로 결정할 수 있다.
일부 개발자는 클래스를 추가하면 JavaScript의 독창성이 사라지고 프로토타입을 사용하는 것이 클래스가 필요한 작업을 수행하는 더 유연한 방법이라고 주장할 수 있다. 다른 객체 지향 프로그래밍 언어의 클래스와 달리 JavaScript는 개인 변수 선언과 같은 기본 클래스 기능을 제공하지 않기 때문.
ECMASCRIPT 2020은 이러한 문제 중 일부를 해결하는 것을 목표로 합니다.

매년 JavaScript 사용자의 요구를 충족시키기 위해 JavaScript에 추가 및 수정이 이루어집니다. 최신 수정 사항은 ECMASCRIPT 2020에 있다. 2020년 클래스에 추가된 일부 항목에는 개인 클래스 변수와 정적 필드가 포함된다.

#### private 클래스 변수

변수 선언이 많은 대규모 코드베이스에서 작업할 때 클래스 내에서만 액세스할 수 있는 변수가 필요할 수 있다. 개인 클래스 변수는 이 문제를 해결한다. 변수명 앞에 해시를 추가하면 쉽게 private 변수를 생성할 수 있습니다.

```js
class ClassWithPrivateField {
#privateField;
}

class ClassWithPrivateMethod {
#privateMethod() {
return "hello world";
}
}

class ClassWithPrivateStaticField {
static #PRIVATE_STATIC_FIELD;
}
```

```js
class Detail {
#name = "steven";

welcome() {
console.log(this.#message);
}
}
```

이전 버전의 JavaScript에서는 새 클래스 인스턴스를 생성하지 않고 'welcome' 메서드에 액세스하려는 시도가 불가능하지만 최신 추가 기능으로 인스턴스 생성 없이 이러한 메서드에 접근할 수 있다.

```js
const DetailMethod = Detail.welcome();
```

### JavaScript에서 Class를 사용해야할까?

oop는 소프트웨어 개발의 복잡성을 줄이기 위해 개발되었다. => 이는 궁극적으로 요구사항을 실행으로 변환하는데 도움이 되는 도구이다.
캡슐화, 추상 데이터 유형, 상속 등은 개발자가 프로그램 구조에 대해 추론하기 위한 것.

```js
class Person {
constructor(name) {
this.name = name;
}

talk() {
console.log(`${this.name} says hello`);
}
}
```

```js
function Person(name) {
this.name = name;
}
Person.prototype.talk = function () {
console.log(`${this.name} says hello`);
};
```

talk함수가 Person 클래스의 encapsulated 메서드가 아니라는 것이다. Person 객체가 상속하는 일반 JavaScript 함수입니다.

또한 `this` 함수가 정의된 위치가 아니라 함수가 호출되는 방식과 관련이 있다.

객체가 불렸을 때 this가 어떻게 동작하는지가 관건.

```js
const Grey = new Person("Grey");
const mockDomObj = {};
mockDomObj.onClick = Grey.talk;
mockDomObj.onClick(); // this.name -> undefined!
//mockDomObj에서 name을 찾아서
```

JavaScript에서 클래스가 작동하는 방식은 다른 클래스 기반 언어에서 구현되는 캡슐화와 상당히 다르다.
JavaScript를 조금 아는사람이라면 bind로 this를 특정시켜줄수 있다는 것을 알지만 디자인 표현을 강요하기 위해 우리의 도구로 싸우는 것이 유용한지에 대한 의문이 든다.

클래스는 객체를 만들기위한 설계도이다. (객체를 만드는 공장같은 역할을 한다.)
이런식으로 이해하면 새로운 방법을 준다. => **factory functions**(object를 리턴해주는 함수)

```js
const PersonFactory = (name) => ({
talk() {
console.log(`${name} says Hello`);
},
});
```

클로저를 통해 this나 bind를 사용하지 않아도 된다.
하지만 이 시점에서 일부 사람들은 클래스가 확장 가능해야 하며, 이러한 팩토리가 쉽게 확장 가능하지 않은 것 같다고 이의를 제기할 수 있다. 하지만 합성과 상속의 이유는 디자인 표현을 개선하기 위함이 아니라 코드 중복을 피하기 위함이라고 생각한다.
==>`extends` 대신 팩토리에서 반환된 객체가 생성되는 방식을 변경

```js
const Mammal = {
isVertebrae: true,
};
const PersonMammalFactory = (name) =>
Object.assign({}, Mammal, PersonFactory(name));
```

이런식으로 assign을 사용
하지만 생성되는 모든 객체에 talk라는 함수가 생성이 된다. 적은 코드에서는 문제가 없지만 코드가 커지게 되면 문제가 생길 수 있고, class와 bind가 이때는 더 적절한 방법일 수가 있다.

[참고링크](https://blog.logrocket.com/the-bleeding-edge-of-javascript-classes/)
[참고링크](https://medium.com/@vapurrmaid/should-you-use-classes-in-javascript-82f3b3df6195)