본문 바로가기
Language/JavaScript (Modern)

[ES6+] 클래스(Class)문법에 대하여

by 썸머워즈 2022. 1. 7.
반응형

ES6부터는 자바스크립트 또한 Class문법을 사용할 수 있게 되었다.

 

자바스크립트에서의 Class는 함수라는 것을 알아두고 클래스(Class)문법에 대해 하나씩 알아가보자.


1. 클래스(Class) 정의

1-1. 클래스 선언

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

우선 클래스를 선언하는 방식은 매우 간단한데, 위 예제처럼 [ Class 클래스명 ] 구조로 선언해주면 된다.

 

위에서 클래스를 함수라고 설명했지만 예외가 있는게, 클래스는 호이스팅(Hoisting)이 불가능하기 때문에 반드시 먼저 선언하고 인스턴스를 생성하거나 호출해야한다.

 

1-2. 클래스 표현식

// unnamed
let Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 출력: "Rectangle"

// named
let Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 출력: "Rectangle2"

클래스 표현식은 클래스를 정의하는 또 다른 방법이다.

위 예제를 보면 알겠지만 클래스 표현식은 이름을 가질 수도 있고, 갖지 않을 수도 있다.

 


2. Class body와 메서드 정의

- Class body는 중괄호 {} 로 묶여 있는 안쪽 부분을 의미하며, 여기에 생성자(constructor)나 메서드 그리고 필드값을 정의하여 사용할 수 있다.

- 지금까지 스크립트 구문은 언제나 항상 다른 언어에 비해 느슨한 부분이 있었는데, 클래스의 본문엄격모드(strict mode)에서 실행된다. 즉, 여기에 적힌 코드는 성능 향상을 위해 더 엄격하게 관리하겠다는 의미이다.

 

2-1. 생성자(Constructor)

// 클래스 선언
class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

// 인스턴스 생성
const square = new Rectangle(10, 10);
console.log(square); // Rectangle {height: 10, width: 10}

생성자(Constructor)는 인스턴스를 생성하고 클래스 필드를 초기화하기 위한 특수한 메서드이다.

생성자는 클래스 내에 한개만 존재할 수 있으며, 인스턴스를 생성할 때 new 연산자와 함께 호출한 것이 생성자이다.

(자바를 기준으로 보자면 클래스명과 생성자 명은 동일한데 자바스크립트 문법에서는 생성자명이 생략되어 construcotr만 선언되어 있는것 같다.)

 

여기서 생성자는 생략이 가능하지만, 어차피 생략해봤자 필수 이기 때문에 자동으로 constructor() {}가 포함되어 있다.

 

그리고 나중에 설명할 부분이지만 클래스에서는 상속이 존재하는데,

클래스에서 상위 클래스의 생성자를 호출할 때 super 키워드를 사용한다. (나중에 상속과 같이 다뤄보자.)

 

2-2. 메서드(Method)와 정적 메서드(Static Method) 그리고 필드

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  
  // Static
  static calcAreaName1 = "custom";
  static calcArea1(height, width) {
    return (height + 100) * width;
  }
    
  calcAreaName2 = "normal"
  calcArea2() {
    return this.height * this.width;
  }
  
}

const square = new Rectangle(10, 10);
console.log(square.calcArea1); // undefined
console.log(square.calcAreaName1); // undefined
console.log(square.calcArea2()); // 100
console.log(square.calcAreaName2); // normal

console.log(Rectangle.calcArea1(10, 10)) // 1100
console.log(Rectangle.calcAreaName1) // custom

 

위 예제에서 메서드와 정적 메서드를 다루었다.

메서드 같은 경우에는 클래스의 인스턴스화를 통해 호출이 가능하며,

정적 메서드 같은 경우에는 클래스의 인스턴스화를 통해 호출이 불가능하고 클래스에 직접적으로 접근하여 호출할 수 있다.

 

* 설명은 메서드를 기준으로 작성하였지만 위 예제처럼 클래스 필드도 static에 대해서 동일하다.

 

2-3.  Public과 Private 필드

class Rectangle {
  height = 0;
  width;
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

필드를 먼저 선언함으로써 self-documenting 역할을 함과 동시에 public 멤버 변수의 역할을 수행한다.

public 필드는 다른 예제에서도 봤겠지만 생성자나 함수를 통해 동적으로 필드를 생성할 수 있다.

 

class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}

// Uncaught SyntaxError: Private field '#height' must be declared in an enclosing class
class Rectangle {
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}

위 예제처럼 # 을 사용하여 private필드를 선언할 수 있고,

클래스 내부에서만 읽고 쓰기가 가능해 외부에서 접근이 불가능하다.

 

private필드를 사용할 때 주의할 점
class body에서 무조건 맨 앞에 선언되어야 한다는 점과 생성자나 함수를 통해 나중에 동적으로 생성할 수 없다.

 

그리고 private 필드 역시 static과 합쳐서 사용이 가능하다.

 

* 여기서 잠깐 의문인점에 대해 잠깐 정리하고 가려고 한다.

왜 private과 public은 자바에서는 접근제한자라며 메서드나 변수에 전부 사용이 가능한데, ES6에서는 필드라고 정의한 것인지 의문이 들었다. 메서드에서는 사용이 불가능한 것일까?

class Calculator {
  a = 0;
  #b = 1;
  
  plus() {
    return this.#b;
  }
  
  minus() {
    return this.a;
  }
  
  #accumulator(x) {
    return x + x;
  }
  
  test(){
    return this.#accumulator(5);
  }
}

const calc = new Calculator();
console.log(calc); // Calculator {a: 0, #accumulator: ƒ, #b: 1}

간단하게 테스트를 해본 결과 메서드에서도 사용이 가능하지만, 뭔가 console.log()에 필드만 나오게 되는데 private method를 선언해준게 console.log()에 출력되는걸 보면 필드로 인식하나보다.


2-4.  getter 와 setter

자바에서는 private 접근제한자를 사용하기위해 getter와 setter를 사용하는데, 

ES6 클래스 문법에도 역시 getter와 setter가 존재하기는 한다.

class Rectangle {
  constructor(height, width) {
    this._height = height;
    this._width = width;
  }
  
  // Setter
  set width(elem) {
    this._width = elem;
  }
  
  set height(elem) {
    this._height = elem;
  }
  
  // Getter
  get area() {
    return this.calcArea();
  }
  
  // 메서드
  calcArea() {
    return this._height * this._width;
  }
}

const square = new Rectangle(10, 10);

console.log(square); // Rectangle {_height: 10, _width: 10}
console.log(square.area); // 100

square.width = 200;
square.height = 30;
console.log(square.width); // undefined
console.log(square.area); // 6000

 

getter 와 setter 각각 get 과 set 이라는 키워드를 메서드 이름 앞에 사용하여 정의한다.

예제 아래를 보면 알겠지만 getter와 setter로 사용된 메서드 이름은 클래스 필드처럼 호출해 사용할 수 있다.

 

즉, getter와 setter는 프로퍼티처럼 참조하는 형식으로 사용하며 참조시에 메서드가 호출되는 형식인 것이다.

getter는 무조건 return을 해줘야하며, setter는 무조건 하나의 파라미터가 있어야한다.

 

getter와 setter를 통해서 private 필드에 대해 제어하는것 역시 가능하다.

 


3. 클래스 상속

3-1. extends 와 super 키워드의 기본 사용법

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // super class 생성자를 호출하여 name 매개변수 전달
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.

위 예제에서 this.name을 보면 상위 클래스를 상속받아 하위클래스에서 사용된다.

그리고 상속의 특징 중 하나인 오버라이딩이 있는데, speak 메서드를 보면 상위 클래스의 메서드를 하위 클래스가 재정의하여 사용하고 있는것을 볼 수 있다.

 

하위 클래스(Dog)를 보면 생성자(constructor) 부분에서 super 키워드를 통해 상위 클래스로 값을 전달하였는데,

여기서 한가지 중요한 점은 클래스 상속을 진행하였으면 하위 클래스의 constructor에서 super()를 무조건 호출해줘야 한다는 점이다.

 

만약 호출하지 않으면 this에 대한 참조 에러가 발생한다.

class Animal {}

class Dog extends Animal {
  constructor() {}
}

let d = new Dog();
// Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

 

위에서 하위 클래스의 constructor에서 super()를 무조건 호출해줘야 한다고 하였는데, 만약 사용자가 constructor를 생략한다면 자동으로 super()가 실행된다.

 

super 키워드에 대해 조금 더 알아보자면 말 그대로 상속받은 상위 클래스에 접근하기 위함이다.

즉, 상위 클래스의 메서드나 필드에 접근하고자 할때 super 키워드를 사용하면 된다.

class Animal {
  constructor(name) {
    this.name = name
  }
  
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  speak() {
    super.speak();
    console.log(`${this.name} dog.`);
  }
}

let d = new Dog("happy");
d.speak();
// happy makes a noise.
// happy dog.

 

3-2. 정적 메서드 상속

class Animal {
  static speak() {
    return 'War!War!';
  }
}

class Dog extends Animal {
  static speak() {
    return `Dog ${super.speak()}`;
  }
  
  sickSpeak() {
    return `sick Dog ${super.speak()} ...`;
  }
}

console.log(Animal.speak()); // 'War!War!'
console.log(Dog.speak());  // 'Dog War!War!'
console.log(new Dog().sickSpeak());
// Uncaught TypeError: (intermediate value).speak is not a function

간단하게 설명하자면

하위 클래스의 정적 메서드에서는 상위 클래스의 정적 메서드를 참조할 수 있으나,

하위 클래스의 일반 메서드에서는 상위 클래스의 정적 메서드를 참조할 수 없다.

 

3-3. 함수 기반 상속

function Animal (name) {
  this.name = name;
}

Animal.prototype.speak = function () {
  console.log(`${this.name} makes a noise.`);
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name} barks.`);
  }
}

let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.

 

3-4. 객체(Object) 기반 상속

const Animal = {
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
};

class Dog {
  constructor(name) {
    this.name = name;
  }
}

// 이 작업을 수행하지 않으면 speak를 호출할 때 TypeError가 발생합니다
Object.setPrototypeOf(Dog.prototype, Animal);

let d = new Dog('Mitzie');
d.speak(); // Mitzie makes a noise.

클래스 생성자가 없는 객체를 상속받기 위해서는 Object.setPrototypeOf() 메서드를 사용하면 가능하다.


참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes

 

Classes - JavaScript | MDN

Class는 객체를 생성하기 위한 템플릿입니다. 클래스는 데이터와 이를 조작하는 코드를 하나로 추상화합니다. 자바스크립트에서 클래스는 프로토타입을 이용해서 만들어졌지만 ES5의 클래스 의

developer.mozilla.org

 

참고 : https://poiemaweb.com/es6-class

 

Class | PoiemaWeb

자바스크립트는 프로토타입 기반(prototype-based) 객체지향형 언어다. 비록 다른 객체지향 언어들과의 차이점에 대한 논쟁들이 있긴 하지만, 자바스크립트는 강력한 객체지향 프로그래밍 능력들을

poiemaweb.com

반응형


댓글

TOP