Class
ES5까지 자바스크립트에는 클래스가 없었음
따라서 프로토타입 체이닝을 통해 클래스를 비슷하게 구현해왔는데 ES6에서 클래스가 추가
=> 생김새는 클래스 구조지만 엔진 내부적으로는 프로토타입 방식으로 작동
추가 된 이유?
클래스 없이도 상속을 비슷하게 구현했지만 비교적 매커니즘이 복잡
=> 코드 작성, 유지보수가 어려워지고 기존의 객체지향 프로그래밍이 익숙한 프로그래머들에게 진입장벽 발생
생성자 함수와 프로토타입을 이용하면 오류를 발생시킬 가능성이 상대적으로 높음
Syntax Sugar
클래스는 일차적으로 JS의 기존 프로토타입 기반 상속에 대한 문법적 설탕 (MDN 기준)
=> 클래스 문법이 자바스크립트에 새로운 객체 지향 상속 모델을 도입하는 것은 아니니까
다만 이 부분에 있어서는 개발자마다 의견이 분분함....
단순한 문법적 설탕이 아닌 새로운 객체 생성 메커니즘이라고 보는 의견도 있다
생성자 함수와의 차이
new 연산자
생성자 함수에서는 new 연산자 없이 호출하는 경우 일반 함수로서 호출
클래스에서는 new 연산자 없이 호출하는 경우 에러 발생
클래스 표현식으로 정의된 클래스의 경우 다음처럼 기명 클래스 표현식의 클래스 이름으로 인스턴스를 생성 할 수 없음
const Person = class MyClass {};
console.log(MyClass); // ReferenceError: MyClass is not defined
const you = new MyClass(); // ReferenceError: MyClass is not defined
extends, super
생성자 함수는 상속을 지원하는 extends와 super 키워드를 지원하지 않음
=> 프로토타입을 이용한 상속이 어려운 이유
반대로 클래스는 extends와 super 키워드를 지원
상속에 대한 자세한 내용은 후술
호이스팅
선언문으로 정의된 생성자 함수는 함수 호이스팅이, 표현식으로 정의된 생성자 함수는 변수 호이스팅이 발생
클래스 선언문으로 정의한 클래스 또한 함수이기 때문에 런타임 이전에 먼저 평가되어 함수 객체를 생성
다만, const와 let처럼 일시적 사각지대에 빠지기 때문에 호이스팅이 되지만 되지 않는 것 처럼 보임
=> 클래스에 접근하기 전에 클래스를 선언하지 않으면 ReferenceError 발생
strict mode
생성자 함수는 암묵적으로 strict mode가 지원되지 않음
반면 클래스 내의 모든 코드는 암묵적으로 strict mode가 지정 및 실행되며 해제가 불가
[[Enumerable]]
클래스는 constructor, 프로토타입 메서드, 정적 메서드 모두 프로퍼티 어트리뷰트 [[Enuemerable]]의 값이 false
=> 열거되지 않는다
클래스 정의
클래스는 class 키워드를 사용해 정의하며,
생성자 함수처럼 파스칼 케이스를 사용하는 것이 일반적
class Person {}
const Person = class {};
const Person = class MyClass {};
위처럼 표현식 정의가 가능한 이유는 클래스가 값으로 사용할 수 있는 일급 객체이기 때문
따라서 클래스는 변수나 자료구조에 저장할 수 있고, 함수의 매개변수에 전달할 수 있으며, 함수의 반환값으로 사용 가능
메서드
클래스 몸체에서 정의할 수 있는 메서드는 constructor, 프로토타입 메서드, 정적 메서드
클래스에서 정의한 메서드의 특징
- function 키워드를 생략한 메서드 축약 표현을 사용
- 암묵적으로 strict mode로 실행
- for ... in 문이나 Object.keys 메서드 등으로 열거 불가
Constructor
인스턴스를 생성하고 초기화하기 위한 특수한 메서드
class Person {
constructor(name) {
this.name = name;
}
}
클래스 내에 한 개를 초과하여 존재할 수 없고 생략시 빈 constructor가 암묵적으로 정의 됨
=> 단 인스턴스 초기화를 위해선 constructor가 필수
별도의 반환문을 갖지 않으며, 반환문이 있으면 this 반환이 무시되어 인스턴스가 반환되지 못한다
=> 반환문을 반드시 생략할 것
Constructor 내부에 선언한 클래스 필드는 클래스가 생성할 인스턴스에 바인딩
클래스 필드는 해당 인스턴스의 프로퍼티가 되며, 인스턴스를 통해 클래스 외부에서 언제나 참조 가능 => public
프로토타입 메서드
생성자 함수를 통해 인스턴스를 생성하는 경우, 프로토타입 메서드를 생성하기 위해 명시적으로 프로토타입에 메서드를 추가해야 함
function Person(name) {
this.name = name;
}
Person.prototype.say = function () {
console.log(`${this.name}`);
};
const me = new Person("Kim");
me.say(); // Kim
클래스를 사용하면 클래스 몸체의 메서드는 기본적으로 프로토타입 메서드가 됨!
class Person {
constructor(name) {
this.name = name;
}
say() { // 프로토타입 메서드
console.log(`${this.name}`);
}
}
const me = new Person('Kim');
me.say(); // Kim
클래스 몸체에서 정의한 메서드는 인스턴스의 프로토타입에 존재하는 프로토타입의 메서드가 되며, 인스턴스는 프로토타입 메서드를 상속받아 사용할 수 있음
정적 메서드
인스턴스를 생성하지 않아도 호출할 수 있는 클래스 자체에 속하는 메서드
생성자 함수의 경우 명시적으로 생성자 함수에 메서드를 추가해야 하지만 클래스에서는 메서드에 static 키워드를 붙이면 된다
class Person {
constructor(name) {
this.name = name;
}
static say() {
console.log("Hi!");
}
}
Person.say(); // Hi!
const me = new Person("Kim");
me.say(); // TypeError: me.say is not a function
정적 메서드가 바인딩 된 클래스의 인스턴스는 프로토타입 체인 상에 존재하지 않음
=> say 메서드는 Person 클래스의 인스턴스에 속하지 않아...!
따라서 Person 클래스의 인스턴스인 me는 해당 메서드를 호출 할 수 없다
Getter / Setter
Getter와 Setter는 객체지향 프로그래밍에서 사용되는 일종의 메서드
Getter는 객체의 속성 값을 반환하는 메서드이며, Setter는 객체의 속성 값을 설정, 변경하는 메서드
클래스 내에서 Getter 또는 Setter을 정의하고 싶은 경우 메소드 이름 앞에 get 또는 set을 붙여주면 됨
function Person(name) {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
set name(value) {
this._name = value;
}
}
const me = new Person('Kim');
me.name = 'Lee';
me.name; // 'Lee'
클래스 상속
한 클래스의 기능을 다른 클래스에서 재사용 할 수 있도록 해주는 기능
extends
특정 클래스를 다른 클래스의 하위 클래스로 지정하기 위해 사용
class Parent {
// ...
}
class Child extends Parent {
// ...
}
해당 코드는 Child 클래스가 Parent 클래스를 상속
해당 관계를 부모-자식 관계 혹은 슈퍼 클래스-서브 클래스 관계라고 부름
서브 클래스는 슈퍼 클래스의 정적 메소드와 정적 속성, 인스턴스 메소드와 인스턴스 속성을 사용할 수 있음
super
생성자 내부에서 super를 함수처럼 호출하면 부모 클래스의 생성자가 호출
메소드 내부에서 super.porp 을 이용해서 부모 클래스의 prop 속성에 접근 가능
class Person{
constructor(family){
this.family=family;
}
say(){
return this.family;
}
}
class Person2 extends Person{
constructor(family, name){
super(family); //부모 생성자를 가져옴
this.name = name;
}
say(){
// 부모 메소드를 가져와서 사용
// 오버로딩 메소드에서 온전한 부모 메소드를 사용하고 싶을때
return super.say() + this.name;
}
}
var kim = new Person2('Lim', 'Hmyang');
console.log(Lim.say()); // 'LimHmyang'
Private
메서드와 필드명 앞에 #을 붙이면 프라이빗 메서드와 필드 정의가 가능
# 기호를 접두사로 사용하여 메서드와 접근자를 비공개로 설정 가능하며 getter, setter 메서드 또한 비공개로 사용 가능
class Person {
#num = 100 // private 변수
constructor(name) {
this.name = name;
}
#say() { // private 함수
console.log(this.#num); // private 변수 호출
}
}
당연히 클래스 외부에서 private 변수 또는 함수로 접근하고자 하면 에러 발생
또한 private 필드와 메서드는 상속이 불가능
syntax sugar 읽는 사람 또는 작성하는 사라밍 편하게 디자인 된 문법
'FE Study' 카테고리의 다른 글
| SEO (0) | 2024.08.24 |
|---|---|
| [FE] webpack vs vite (0) | 2024.08.16 |
| [FE] JavaScript에서의 프로토타입(Prototype) (0) | 2024.08.09 |
| [FE] JavaScript에서의 클로저(Closure) (0) | 2024.08.09 |
| [FE] JavaScript에서의 this (0) | 2024.08.09 |