클래스

seeen·2023년 1월 5일
0
post-thumbnail

이 글은 '이웅모'님의 '모던 자바스크립트 Deep Dive' 책을 통해 공부한 내용을 정리한 글입니다. 저작권 보호를 위해 책의 내용은 요약되었습니다.

클래스 (Class)

자바스크립트는 프로토타입 기반 객체지향 언어이다. 따라서 클래스가 필요없는 객체 지향 스타일로 타 객체 지향 언어의 상속, 캡슐화 등의 개념을 구현할 수 있었다. ES5에서는 아래 코드와 같이 클래스를 구현하였다.

ES6에 들어서면서 타 객체 지향 언어와 비슷하게 클래스를 지원하였다. 기존의 프로토타입 기반의 객체 지향 모델을 폐지한 것은 아니며, 그렇다고 새로운 객체 지향 모델을 제공하는 것 또한 아니다. 클래스도 함수이며, 일각에서는 기존의 프로토타입 기반 패턴의 문법적 설탕(Syntactic Sugar)이라고 보기도 한다. 하지만 클래스가 생성자 함수와 동일하게 동작하지 않으며 더 엄격하다는 점에서 차이를 보인다.

ES5에서 OOP

ES5에서는 생성자 함수와 프로토타입, 클로저를 활용하여 다음과 같이 객체 지향 프로그래밍을 구현하였다.

var Fruit = (function() {
  
  // constructor
  function Fruit(price) {
    this._price = price;
  }
  
  // public static method
  Fruit.helloWord = function () {
    console.log('Hello, World!');
  }
  
  // public instance method
  Fruit.prototype.getPrice = function () {
    console.log('Price : ' + this._price);
  }
  
  // return constructor
  return Fruit;
}());

var apple = new Fruit(1000);

Fruit.helloWord(); // Hello, World!
apple.getPrice(); // Price : 1000

console.log(apple instanceof Fruit); // true

// 인스턴스 추가 생성
// var banana = new Fruit(2000);

위 코드를 그림으로 정리하면 다음과 같이 나타낼 수 있다.

클래스 구조와 특성

ES6 클래스는 class 키워드를 사용하여 정의한다. 생성자 함수와 마찬가지로 파스칼 케이스를 사용하는 것이 일반적이나, 그렇지 않아도 에러는 발생하지 않는다.

class Fruit {}
class fruit {} // Non Error

인스턴스 생성new 연산자와 함께 클래스 이름을 호출하면 된다.

class Example {}

const exam = new Example();

클래스 필드

기존에는 클래스 몸체에는 메소드만 선언이 가능했으며 필드(멤버 변수)를 선언하면 SyntaxError가 발생했다. 하지만, Chrome 72 또는 Node 12 이상의 버전에서 동작하면 정상적으로 실행된다. 또한 # 기호를 통해 private 접근제한자를 설정해줄 수 있다.

// Chrome 72 또는 Node 12 이상의 환경

class Hello {
  hi = 'hi'; // public field
  #hello = 'hello'; // private Field
  static bye = 'bye'; // public static field
  static #goodBye = 'good bye'; // private static field
  
  // ...
  
}

const hello = new Hello();

console.log(hello.hi); // hi
console.log(hello.#hello); // SyntaxError: Private field '#hello' must be declared in...
console.log(Hello.bye); // bye
console.log(Hello.#goodBye); // SyntaxError: Private field '#goodBye' must be declared in...

클래스 생성자 (constructor)

인스턴스를 생성하고 클래스 필드를 초기화하기 위한 특수한 메소드이다.

  • 클래스 내 오직 한 개의 constructor만 존재
  • 생략 가능
class Fruit {
  name;

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

const apple = new Fruit('apple');

console.log(apple.name) // apple

클래스 메서드

메서드는 크게 인스턴스 메서드와 정적 메서드로 나눌 수 있다.

  • 정적 메서드
    • 클래스에 귀속
    • 절차지향성, 유틸성 (Math 등)
  • 인스턴스 메서드
    • 미래에 생성할 인스턴스에 귀속
    • 객체지향성

소목차 ES5에서 OOP에서 사용한 예제 코드를 ES6 Class 문법으로 재구현 해보자.

class Fruit {
  price;
  
  constructor(price) {
    this.price = price;
  }
  
  static helloWord() {
    console.log('Hello, World');
  }
  
  getPrice() {
    console.log(`Price : ${this.price}`);
  }
}

const apple = new Fruit(1000);

Fruit.helloWord(); // Hello, World
apple.getPrice(); // Price : 1000

console.log(apple instanceof Fruit); // true

그림으로 나타내면 다음과 같다.

클래스 상속

객체 지향 프로그래밍(OOP)의 다형성과 관련이 깊은 내용으로, 새롭게 정의할 클래스가 기존의 클래스와 유사하다면 상속을 통해 다른 점만 구현하면 된다. 코드 재사용에 있어서 유용하다.

extends 키워드

부모 클래스(Base Class)로부터 상속받아 자식 클래스(Sub Class)를 정의할 때 사용한다.

super 키워드

부모 클래스를 참조할 때 또는 부모 클래스를 호출할 때 사용한다. 자식 클래스의 constructor 내부에서 super 메서드를 호출하지 않으면 this 참조 에러가 발생한다.

다음 코드를 보자.

class Fruit {
  name;
  static type = 'Fruit';

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

  getName() {
    return this.name;
  }

  static foodType() {
    return Fruit.type;
  }
}

class Apple extends Fruit {
  taste;
  static shape = 'Circle';

  constructor(name, taste) {
    super(name);
    this.taste = taste;
  }

  getTaste() {
    return this.taste;
  }

  static foodShapeType() {
    return `${Apple.shape} in ${super.foodType()}`;
  }

  // 자식 클래스의 인스턴스는 부모 클래스의 정적 메서드 참조 불가 (프로토타입 체인 상이) 
  // getFoodType() {
  //   return `${super.foodType()}`;
  // }
}

const apple = new Apple('apple', 'sweet');

console.log(apple instanceof Fruit); // true

console.log(apple.getTaste()); // sweet
console.log(apple.getName()); // apple

console.log(Apple.foodShapeType()); // Circle in Fruit
console.log(Apple.foodType()); // Fruit

// console.log(apple.getFoodType()); // TypeError: (intermediate value).foodType is not a function

이를 그림으로 나타내면 다음과 같다.

그 외

  • 오버라이딩(Overriding) : 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의
  • 오버로딩(Overloading) : 같은 이름의 메서드를 매개변수 타입, 또는 매개변수 개수를 다르게 구현하여 호출

자바스크립트에서 오버라이딩은 지원하나, 오버로딩은 지원하지 않는다. 다만, arguments 객체를 활용하여 구현할 수는 있다.

  • get : getter, 반드시 무언가를 반환하는 것이 좋다.
  • set : setter, 필드 값 할당 시 사용한다.

이 둘은 필드 이름처럼 사용되는 것에 주의하자.

class Fruit {
  name;
  
  constructor(name) {
    this.name = name;
  }
  
  set changeFruitName(newName) {
    this.name = newName
  }
  
  get nameOfFruit() {
    return this.name;
  }
}

const banana = new Fruit('banana');

banana.changeFruitName = 'choco banana';
console.log(banana.nameOfFruit); // choco banana
profile
woowacourse FE 5th, depromeet Web 15th

0개의 댓글