자바스크립트의 Class 문법은 ES6에 추가된 문법이다.
추가되기 전에도 생성자 함수와 프로토타입을 통해 상속을 구현할 수 있었는데 굳이 추가된 이유가 뭘까?
👀 Class가 나오게 된 배경
프로토타입 체인을 이용해서 클래스 방식을 모방할 수 있지만, 구현이 복잡하고 어렵다.
또한 기존 객체지향 프로그램 언어들과 달라서 개발자들이 어려움을 느꼈다.
결론적으로 개발자들의 편의를 위해 도입되었다는 것이다!
👀 Class vs 생성자 함수
- new 연산자
생성자 함수는 new 연산자를 붙이지 않고 객체를 생성해도 오류가 발생하지 않지만,
class는 new 연산자를 붙이지 않고 객체를 생성하면 오류가 발생한다.
생성자 함수에서 오류가 발생하지 않을뿐, 의도했던 동작과는 다르게 단순 함수 호출로 처리된다.
- extends, super
생성자 함수에서 프로토타입 체인을 이용해서 상속을 구현할 수는 있다.
class에서는 extends 키워드로 다른 클래스를 확장할 수 있고,
super 키워드를 사용해 부모 클래스의 메소드를 상속받고 호출할 수 있다.
- 호이스팅
생성자 함수는 호이스팅이 발생하지만, 클래스는 호이스팅이 발생하지 않는 것처럼 동작한다.
- strict mode
클래스 내의 모든 코드는 암묵적으로 strict mode로 실행되며, 해제할 수 없다.
클래스는 생김새만 클래스 구조이고 엔진 내부적으로는 프로토타입 방식으로 작동한다.
👀 Class 선언과 메서드 정의
먼저 생성자 함수로 구현한 걸 보자.
function Person(name, age) {
this.name = name;
this.age = age;
this.showName = function() {
console.log(`이름은 ${this.name}, 나이는 ${this.age}`);
}
};
const heedong = new Person("heedong", 20);
heedong.showName();
아래는 class 문법으로 바꾼 코드이다.
class Person2 {
constructor(name, age) {
this.name = name;
this.age = age;
}
showName() {
console.log(`이름은 ${this.name}, 나이는 ${this.age}`);
}
};
const heedong2 = new Person2("heedong2", 20);
heedong2.showName();
class에서는 constructor가 사용되었는데, 이는 객체를 만들어주는 생성자 메소드이다.
🤔 생성자 함수와 class로 생성한 각 객체를 더 자세히 살펴보자.
- 생성자 함수로 생성한 heedong 객체를 보면,
객체 내부에 showName 함수가 저장되어 있다. - 클래스로 생성한 heedong2 객체를 보면,
Person2 클래스의 프로토타입 객체에 저장되어 있다.
🤔 둘의 차이점은?
생성자 함수 내부에 선언한 함수는 객체가 각각 독립적으로 가지게 되므로 메모리 측면에서 비효율적이다.
클래스 함수 내부에 선언한 함수는 클래스의 프로토타입 객체에 저장되기 때문에,
해당 클래스로 생성된 모든 객체가 메서드를 공유하므로 메모리 측면에서 효율적이다.
또한, for...in 반복문 사용에서도 다르다.
for(const p in heedong){
console.log(p);
}
위의 출력 결과는
age
name
showName 이고,
for(const p in heedong2) {
console.log(p);
}
위의 출력 결과는
age
name 이다.
class의 메서드는 for...in 문에서 제외되는 특징을 볼 수 있는데,
프로토타입 메서드는 객체의 열거 가능한 속성 목록에 포함되지 않기 때문이다.
👀 Getter / Setter
ES6부터 추가된 문법으로,
객체 리터럴 안에서 속성 이름 앞에 get/set 키워드를 붙이면 Getter/Setter를 정의할 수 있다.
const person = {
name: 'heedong',
age: 20,
get userName() {
return person.name;
},
set userName(value) {
person.name = value;
}
}
console.log(person.userName);
Getter/Setter는 함수 호출 형식이 아니라, 일반 프로퍼티처럼 접근해서 사용한다.
userName이라는 가상의 프로퍼티가 생기게 되는데
위처럼 읽고 쓸 순 있지만 실제로는 존재하지 않는 프로퍼티이다.
실제로 userName이라는 속성은 존재하지 않지만 person.userName으로 접근이 가능하고
userName에는 객체의 name 속성이 저장되어 있다.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
get age() {
return this.age;
}
set age(value) {
this.age = value;
}
}
const heedong = new Person("heedong", 20);
console.log(heedong.age);
위의 코드를 실행시키면
RangeError: Maximum call stack size exceeded at set age
콜 스택을 초과했다는 오류가 난다!
getter를 정의하는 순간, this.age는 메모리에 올라가 있는 age를 읽어오는게 아니라 getter를 호출하고,
setter를 정의하는 순간, 값을 할당할 때 바로 메모리에 값을 할당하는게 아니라 setter를 호출한다.
따라서 무한 위의 코드는 무한 재귀를 유발한다.
무한 재귀를 막기 위해서는 변수 앞에 _를 붙여서 많이 사용한다.
get age() {
return this._age;
}
set age(value) {
this._age = value;
}
👀 Private
ES2019에 추가된 문법이다.
변수나 메소드 명 앞에 #을 붙이는 방식으로 사용한다.
public 필드는 어디서든지 접근할 수 있다. 우리가 지금까지 써왔던 방식이다.
private 필드는 클래스 내부에서만 접근할 수 있다.
class Person {
constructor(name, age) {
this.#name = name;
this.#age = age;
}
}
const heedong = new Person("heedong", 20);
console.log(heedong.name);
당연히 클래스 외부에서 heedong.name으로 접근하면 오류가 난다.
private 필드와 메서드는 상속되지 않는다.
👀 Static
클래스에서 공통적으로 사용하는 것이라면 static을 사용하면 된다.
static 키워드를 사용하면 object마다 할당되는게 아니라 클래스 자체에 할당된다.
따라서 static으로 선언된 변수나 메서드를 호출할때는 object 이름이 아닌 클래스 이름을 이용해서 호출해야 된다.
또한 인스턴스 객체를 생성하지 않아도 static 메서드를 사용할 수 있다는 장점이 있다.
class Person {
static age = 20;
constructor(name) {
this.name = name;
}
}
const heedong = new Person("heedong");
console.log(heedong.name); // heedong
console.log(heedong.age); // undefined
console.log(Person.age); // 20
전역으로 사용할 유틸리티 함수를 생성할 때 많이 사용한다.
👀 상속
생성자 함수는에서는 프로토타입을 이용해서 상속을 구현했다.
클래스에서는 extends 키워드 사용하기만 하면 상속이 된다!
상속을 통해 생성된 클래스는 부모 클래스의 모든 프로퍼티를 가진다.
- 메서드 오버라이딩
동일한 이름의 메소드가 있으면 덮어쓴다.
부모 클래스의 메서드를 그대로 사용하면서 다른 내용을 추가하고 싶으면
super.메소드명();을 호출한 뒤, 추가하면 된다.
- 생성자 오버라이딩
부모 생성자를 반드시 먼저 호출해야 된다.
일반 클래스의 constructor는 빈 객체를 만들어주고 this에 이 객체를 할당한다.
반면 extends로 만들어진 자식 클래스는 이 작업을 건너뛴다.
자식 클래스의 생성자에서 이렇게 밑줄 친 부분이 존재하는 것 처럼 동작하는데,
자식 생성자에 super가 없으면 위와 같이 동작하지 않기 때문에 super();로 호출해줘야 하는 것이다.
자식 클래스의 생성자에서 super()를 작성해야지 this 객체가 만들어지는 것이다.
참고로 자바스크립트에서는 다중 상속을 지원하지 않는당.
'JavaScript' 카테고리의 다른 글
RequestAnimationFrame (0) | 2024.10.08 |
---|---|
JS Array와 Array 메소드 (0) | 2024.08.26 |
JS Prototype (0) | 2024.08.08 |
JS closure (0) | 2024.08.07 |
JS This (0) | 2024.08.07 |