프로그래밍 언어/JavaScript

모던 자바스크립트(ES6+) : 클래스(class)

bewisesh91 2022. 2. 10. 10:56
728x90

자바스크립트의 클래스는 ES6에서 도입되었다.

class 키워드를 사용해서 객체의 틀을 정의하고, 그 앞에 new를 붙여서 객체를 생성하는 방법이다.

class를 사용할 때는 보통 프로퍼티의 경우 constructor 안에 정의하고, 메서드의 경우 constructor 밖에 정의한다.

'use strict';
// Object-oriendted programming
// class: template
// object: instance of a class
// JavaScript classes
//  - introduced in ES6
//  - syntactical sugar over prototype-based inheritance

// 1. Class declarations
class Person {
  // constructor
  constructor(name, age) {
    // fields
    this.name = name;
    this.age = age;
  }

  // methods
  speak() {
    console.log(`${this.name}: hello!`);
  }
}

const seunghyun = new Person('seunghyun', 20);
console.log(seunghyun.name);
console.log(seunghyun.age);
seunghyun.speak();

그런데 클래스 내의 내용을 사용자가 임의로 바꾸거나, 의도와 다르게 조작해서는 안된다. 이를 위해 Getter와 Setter 메서드를

이용할 수 있는데, 해당 메서드를 통해 필드에 접근해야 하며, 보통 _를 붙여서 만든다. 이를 캡슐화라고 한다. 그러나 _를 사용한다고 해서

완벽한 캡슐화가 된 상태는 아니다. 자바에서 private라는 키워드를 이용하여 캡슐화를 지원하는과 달리, 자바스크립트에는 캡슐화를

자체적으로 지원하는 문법이 아직 없다. 다만 클로저(Closuer)라고 하는 개념을 응용하면 완벽한 캡슐화를 할 수 있다.

클로저란 어떤 함수와 그 함수가 참조할 수 있는 값들로 이루어진 환경을 하나로 묶은 것을 의미한다. 다만, 이를 이용하여 캡슐화를 

구현해야하는지는 논란이 많다. 을 의미

// 2. Getter and Setter
class User {
  constructor(firstName, lastName, age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }

  get age() {
    return this._age; // _를 사용한다.
  }

  set age(value) {
    // if (value < 0) {
    //   throw Error('age can not be negative');
    // }
    this._age = value < 0 ? 0 : value; // _를 사용한다.
  }
}

const user1 = new User('Steve', 'Job', -1);
console.log(user1.age);

또한, public과 private 필드를 구분하여 지정할 수도 있다. 이때, private 필드의 경우 앞에 #을 붙이면 된다.

다만, 아직 사용하기에는 이른 감이 있다.

// 3. Fields (public, private)
// Too soon!
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields
class Experiment {
  publicField = 2;
  #privateField = 0;  // privateField의 경우 #을 사용한다.
}
const experiment = new Experiment();
console.log(experiment.publicField);
console.log(experiment.privateField);

Static을 이용하여 프로퍼티와 메서드를 선언할 수 있다.

Static으로 선언한 프로퍼티와 메서드를 사용하기 위해서는 개별 객체가 아닌 클래스를 이용하여 호출해야 한다.

// 4. Static properties and methods
// Too soon!
class Article {
  static publisher = ' Jungle';
  constructor(articleNumber) {
    this.articleNumber = articleNumber;
  }

  static printPublisher() {
    console.log(Article.publisher);
  }
}

const article1 = new Article(1);
const article2 = new Article(2);
console.log(Article.publisher);
Article.printPublisher();

하나의 클래스를 다른 클래스가 상속할 수 있다. 예를 들어 아래와 같이 Shape라는 클래스를 만들고 다른 클래스에서 extends를 이용하여 상속하면 Shape에서 정의한 필드와 메서드를 이용할 수 있다. 그런데 상속받은 것을 바꾸어서 사용하고 싶은 경우가 있을 수 있는데,

그 경우 상속받을 때 수정해서 사용하면 된다. 이를 오버 라이딩이라 한다. 그런데 상속은 상속대로 받고, 일부 내용만 추가하고 싶은 경우,

super를 이용하여 먼저 상속을 받고 추가하고 싶은 내용을 기입하면 된다.

// 5. Inheritance
// a way for one class to extend another class.
class Shape {
  constructor(width, height, color) {
    this.width = width;
    this.height = height;
    this.color = color;
  }

  draw() {
    console.log(`drawing ${this.color} color!`);
  }

  getArea() {
    return this.width * this.height;
  }
}

class Rectangle extends Shape {}
class Triangle extends Shape {
  draw() {
    super.draw();
    console.log('🔺');
  }
  getArea() {
    return (this.width * this.height) / 2;
  }

  toString() {
    return `Triangle: color: ${this.color}`;
  }
}

const rectangle = new Rectangle(20, 20, 'blue');
rectangle.draw();
console.log(rectangle.getArea());
const triangle = new Triangle(20, 20, 'red');
triangle.draw();
console.log(triangle.getArea());

// 6. Class checking: instanceOf
console.log(rectangle instanceof Rectangle);
console.log(triangle instanceof Rectangle);
console.log(triangle instanceof Triangle);
console.log(triangle instanceof Shape);
console.log(triangle instanceof Object);
console.log(triangle.toString());

let obj = { value: 5 };
function change(value) {
  value.value = 7;
}
change(obj);
console.log(obj);