
아래는 같은 내용을 생성자 함수와 클래스로 각각 정의한 예제이다.
자바스크립트의 클래스는 프로그래머의 편의를 위한 문법적 설탕 개념으로, 동작 자체를 분석해보면 결국 생성자 함수 방식으로 동작한다.
** 문법적 설탕
이라는 표현은 자바스크립트 클래스가 내부적으로 프로토타입 기반의 동작을 감싸서 좀 더 직관적인 문법을 제공한다는 것을 의미한다. 즉, 클래스는 함수를 문법적 설탕으로 포장해서 편리한 문법을 제공하는 것이므로, 생성자 함수와 프로토타입을 통해 인스턴스를 생성하는 것과 동일한 방식으로 작동하는 것이다.
// 생성자 함수 (ES5 버전에서 사용하던 방식)
function PersonF(name, age) {
this.name = name;
this.age = age;
}
// 생성자 함수로 인스턴스 생성
const personF = new PersonF("토토로", 140); // PersonF { name: '토토로', age: 140 }
console.dir(personF);
// --------------(위 아래는 같다)----------------
// 클래스 (ES6 버전 이후 사용)
class Person {
constructor(name, age) { // 초기 멤버 속성 정의
this.name = name;
this.age = age;
console.log(name, age);
}
}
// new 키워드를 이용해서 클래스 인스턴스 생성
const personC = new Person("영희", 20);
console.dir(personC); // Person { name: '영희', age: 20 }
클래스를 정의할 때 초기 멤버가 필요하다면 constructor를 사용해야 한다. 강제되는 내용이 아니므로, 멤버 속성이 필요하지 않다면 사용하지 않아도 된다.
상속을 정의하는 키워드이다.
class 상속받는 클래스 extends 상속하는 클래스 형태로 작성하면 된다.
| 생성자 함수 방식 | 클래스 |
|---|---|
| 초기 구문이 비교적 복잡함 | 코드의 간결성 |
| 명시적으로 프로토타입을 설정해야 함 | 상속이 더 쉬움 |

extends를 사용할 때, constructor를 사용한다면 내부에 super()를 반드시 적어줘야 한다. constructor를 정의할 때는 super()를 호출해야만 부모 클래스의 속성을 사용할 수 있기 때문이다.
⇒ 인스턴스에 부모의 인스턴스를 포함시키는 것. 내부적으로 프로토타입 체이닝이 발생하는 것이다.
🤖 super()의 역할에 대해 추가로 설명해줘
super()는 부모 클래스의 생성자를 호출하는 역할을 합니다.자식 클래스에서 부모 클래스의 기능을 상속받고, 그 위에 추가적인 속성이나 메서드를 정의할 때
super()를 호출하여 부모 클래스의 초기화 작업을 수행해야 합니다. 만약super()를 호출하지 않으면 자식 클래스의 this에 접근할 수 없으므로, 자식 클래스의 생성자 내에서this를 사용하는 경우 반드시super()를 먼저 호출해야 합니다.
정적 메서드란, 인스턴스를 사용하지 않고 사용할 수 있는 메서드를 말하며, static을 사용해서 정적 메서드를 만들 수 있다.
static 키워드를 사용하여 정의된 메서드는 클래스 인스턴스를 생성하지 않고도 호출할 수 있다. 정적 메서드란, 인스턴스를 사용하지 않고 클래스 이름을 통해 직접 호출할 수 있는 메서드를 의미한다.
** 정적 메서드의 장점
불필요하게 인스턴스를 사용하지 않고 바로 메서드로 접근하게 해줌으로써, 메모리 효율을 높일 수 있다.
class MathUtils {
// 정적 메서드: 클래스 인스턴스를 생성하지 않고도 호출 가능
static add(n1, n2) {
return n1 + n2;
}
}
// const math = new MathUtils(); // 정적 메서드로 정의된 클래스의 프로퍼티는 이렇게 사용할 수 없다
const sum = MathUtils.add(10, 20); // 정적 메서드는 클래스 이름을 통해 호출해야 한다
console.log(sum); // 30
// ============================
// 생성자 함수로 정적 메서드 생성하기
function Maths() {}
Maths.add = function (a, b) {
return a + b;
};
console.log(Maths.add(10, 20)); // 30
getter / setter는 접근자 혹은 접근자 메서드라고 부른다.
이것은 인스턴스 객체의 속성 값을 바꿀 때, 바꾸는 것을 제어하고 싶은 경우에 사용한다.
set이 사용될 때 반드시 get의 사용이 강제되지는 않는다.
class Car {
constructor(speed) {
this.speed = speed; // 초기 속도 설정 (setter가 호출됨)
}
// setter: speed 속성에 값을 할당할 때 호출됨
set speed(value) {
// 내부에서 다시 speed를 호출하므로 재귀 호출 문제가 발생
this.speed = value < 0 ? 0 : value;
}
}
const car = new Car(200);
car.speed = -100;
console.log(car.speed); // 재귀 호출로 인해 에러 발생
// =========== 해결 방법 ==================
class Car {
constructor(speed) {
this._speed = speed; // 초기 속도 설정
this._color = color;
}
// setter: speed 속성에 값을 할당할 때 호출됨
set speed(value) {
// 내부에서 _speed를 사용하여 재귀 호출을 피함
this._speed = value < 0 ? 0 : value; // speed 값이 음수가 되지 않도록 함
}
// getter: speed 속성을 조회할 때 호출됨
get speed() {
return this._speed;
}
set color(value) {
this._color = value === "white" ? "black" : value;
}
// getter: speed 속성을 조회할 때 호출됨
get color() {
return this._color;
}
}
const car = new Car(200);
car.speed = -100; // => set speed()가 호출되어 _speed 값이 설정됨
console.log(car.speed); // => get speed()가 호출되어 _speed 값을 반환함
클래스 내부에서만 접근할 수 있는 속성으로, # 기호를 붙여서 선언한다.
외부에서는 직접 접근이 불가능하고, 상속된 클래스에서도 마찬가지로 접근할 수 없다.
⇒ 따라서 클래스 내부의 데이터를 안전하게 보호하고, 외부에서 직접 변경하지 못하도록 할 때 사용한다.
class Car {
#speed; // 프라이빗 필드
constructor(speed) {
this.#speed = speed;
}
getSpeed() {
return this.#speed; // 클래스 내부에서만 접근 가능
}
}
const car = new Car(100);
console.log(car.getSpeed()); // 100
console.log(car.#speed); // 오류 발생: 접근 불가
다른 클래스에 상속되어도 프라이빗 필드는 똑같이 유지된다
메서드를 재정의하는 현상(= 덮어쓰기)이다.
class Animal {
sound() {
console.log("sound!");
}
}
class Dog extends Animal {
run() {
console.log("run");
}
sound() {
console.log("dog sound");
}
}
const dog = new Dog();
dog.run();
dog.sound();
// 실행 결과
// run
// dog sound
객체는 아니지만 객체로 취급이 되는 것
일급 객체란, 변수에 할당하거나 함수의 인자로 전달할 수 있는 특성을 가진 객체
// 함수 표현식
const sum = function (num1, num2) {
return num1 + num2;
}
function greet(callback) {
callback();
}
greet(() => {console.log("hello"});
function outer() {
return function(){
console.log("inner");
}
};
const a = outer();
a();
const Func = new Function("name", "return '안녕하세요, ' + name");
console.log(Func("기수"));
메서드 체이닝은 메서드 호출이 끝난 후 객체 자신을 반환하여 다음 메서드를 연속해서 호출할 수 있게 하는 패턴이다. 따라서, 메서드 체이닝을 사용하려면 this를 사용하는 것이 기본이다.
class Builder {
constructor() {
this.value = "";
}
append(str) {
this.value += str;
return this; // 체이닝을 위해 this 반환
}
getValue() {
return this.value; // 최종 값을 반환하고 체이닝 종료
}
}
// 테스트 케이스
const builder = new Builder();
const result = builder.append("Hello, ").append("World!").getValue();
console.log(result); // Hello, World!