모던 자바스크립트(ES6+) : 클래스(class)
자바스크립트의 클래스는 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);