해당 포스팅은 위키북스의 모던 자바스크립트 Deep Dive라는 책을 독학하며 기록하는 글입니다.
ES6에서 도입된 클래스는 기존 프로토타입 기반 객체지향 프로그래밍과 같이 프로토타입 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지는 않으며, 생성자 함수보다 엄격하며 생성자 함수에서 제공하지 않는 기능도 제공한다.
다음은 기존의 생성자 함수를 이용한 방식과 클래스 방식과의 몇 가지 차이점이다.
클래스의 정의는 다음과 같이 간단하다.
class Person { }
클래스는 함수이다. 따라서 값처럼 사용할 수 있는 일급 객체이며 일급객체의 특징 또한 모두 가지고 있다.
클래스를 사용해서 인스턴스를 생성하는 법은 생성자 함수와 같이 new
키워드를 같이 사용하면 된다.
class Person { }
const man = new Person();
클래스 몸체에는 0개 이상의 메서드를 선언할 수 있으며 정의할 수 있는 메서드는 constructor(생성자), 프로토타입 메서드, 정적 메서드 세 가지가 있다.
class Person {
constructor(name) {
this.name = name;
}
}
const man = Person("Han");
console.log(man.name); // "Han"
prototype
이라는 키워드를 함수명과 메서드 사이에 써줬어야 했다. 하지만 클래스에서는 그냥 내부에 함수를 생성하면 프로토타입 메서드가 된다.class Person {
sayHi() {
console.log("Hi");
}
}
cosnt man = new Person();
man.sayHi(); // "Hi"
static
이라는 키워드를 붙혀주면 해당 메서드는 정적 메서드가 된다.class Person {
static sayHi() {
console.log("Hi");
}
}
cosnt man = new Person();
man.sayHi(); // TypeError: man.sayHi is not a function
Pserson.sayHi(); // "Hi"
다음은 프로토타입 메서드와 정적 메서드의 차이이다.
클래스가 인스턴스를 생성하는 과정은 다음과 같다.
클래스가 new 키워드와 같이 호출이 되면 constructor가 실행되기 이전에 암묵적으로 빈 객체 {}
가 생성이 된다. 그리고 해당 빈 객체에 this가 바인딩된다.
그 다음 constructor 메서드가 실행되어 객체를 초기화하는데, constructor가 생략된 클래스라면 2번 과정역시 생략되어 그대로 빈 객체를 인스턴스로 반환하게 된다.
constructor가 생략되지 않고 인스턴스롤 초기화하는 코드가 있다면 해당 코드를 실행시켜 인스턴스를 초기화한다.
클래스의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적을 반환된다.
클래스내의 프로퍼티 종류로는 인스턴스 프로퍼티와, 접근자 프로퍼티가 있으며 다음과 같다
인스턴스 프로퍼티 : 인스턴스 프로퍼티는 항상 constructor 메서드 내에서 정의해야 한다. 이는 클래스가 생성할 인스턴스의 프로퍼티를 정의하는 것으로 인스턴스 프로퍼티를 통해 암묵적으로 생성된 빈 객체가 초기화된다.
접근자 프로퍼티 : 접근자 프로퍼티는 자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티이다.
getter함수와 setter함수로 구성되어 있으며 getter는 인스턴스의 프로퍼티에 접근할 때마다 프로퍼티 값을 조작하거나 별도의 행위가 필요할 때, setter는 인스턴스 프로퍼티에 값을 할당할 때마다 프로퍼티 값을 조작하거나 별도의 행위가 필요할 때 사용된다.
getter함수는 getter함수로 만들고 싶은 메서드 앞에 get
키워드를 붙혀서 만들고, setter는 set
키워드를 붙혀서 만든다.
class Person {
constructor(name) {
this.name = name;
}
get UserName() {
return `${this.name}`;
}
set UserName(name) {
this.name = name;
}
}
클래스 필드란 클래스 기반 객체지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 가리키는 용어이다. 하지만 기본적으로 자바스크립트에서는 인스턴스 프로퍼티를 constructor내에서 this를 통해 프로퍼티를 추가할 수 있는데, 2021년 1월 TC39 프로세스의 stage3에 제안되어 있는 "Class field declarations"를 통해 자바스트립트에서도 인스턴스 프로퍼티를 마치 클래스 기반 객체지향 언어처럼 클래스 몸체에 바로 정의할 수 있게 되었다. (물론 아직 최신브라우저(Chrome72이상) 또는 최신 Node.js(버전12이상)에서만 가능하다)
class Person {
name = "Han";
}
const man = new Person();
console.log(man.name); // "Han"
하지만 몇 가지 제약이나 특징이 있는데 다음과 같다.
자바스크립트에서는 다른 클래스 기반 객체지향 언어와 같이 private, public, protected 키워드를 제공하지 않는다. 따라서 모든 인스턴스 프로퍼티는 외부에서 참조할 수 있는 public상태이다.
#
을 붙히는 것이다. 당연히 클래스 내부에서 해당 프로퍼티를 참조할 때도 #
를 붙여야 한다.class Person {
#name;
constructor(name) {
this.#name = name;
}
get UserName() {
return this.#name;
}
const man = Person("Han");
console.log(man.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class
console.log(man.UserName); // "Han"
class Person {
static type = "사람";
}
console.log(Person.type); // "사람"
ES6부터 추가된 클래스를 통해 자바스크립테에서도 이제 extends
키워드를 통해 클래스를 이용한 상속이 가능해졌다. 사용법은 다음과 같다.
class Base { } // 부모 클래스
class Derived extends Base { } // 자식 클래스
super
는 수퍼클래스의 constructor를 호출하는 키워드로 ...args
를 통해 new 키워드와 함께 클래스를 호출할 때 받은 인수를 그대로 전달한다.construdctor(...args) {
super(...args);
}
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
toString() {
return `width = ${this.width}, height = ${this.height}`;
}
}
class ColorRectangle extends Rectangle {
constructor(width, height, color) {
super(width, height);
this.color = color;
}
toString() {
rreturn super.toString() + `, color = ${this.color}`;
}
}
extends
키워드 앞에는 항상 클래스가 와야 하지만 뒤에는 클래스가 올 수도 있고, 생성자 함수가 올 수도 있다. 따라서 extends를 사용해 표준 빌트인 생성자 함수인 Math나 Array 등을 상속받아 자신만의 클래스를 만들 수 있다.