• 자바스크립트는 프로토타입 기반 객체지향 언어다.
    ES5에서는 다음과 같이 생성자 함수와 프로토타입을 통해 객체지향 언어의 상속을 구현할 수 있었다.
    // ES5 생성자 함수
    var Person = (function() {
      // 생성자 함수
      function Person(name) {
        this.name = name;
      }
      
      // 프로토타입 메서드  
      Person.prototype.getName = function() {
        return this.name;
      };
        
      return Person; // 생성자 함수 반환
    }());
      
    // 인스턴스 생성
    var person1 = new Person('Jung'); 
    console.log(person1.getName()); // Jung
    
  • 클래스와 생성자 함수는 모두 프로토타입 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지는 않는다.
  • 클래스는 생성자함수보다 엄격하며 생성자 함수에서는 제공하지 않는 기능도 제공한다.

생성자 함수와 클래스 차이

1) 클래스를 new 연산자 없이 호출하면 에러가 발생한다.
생성자 함수를 new 연산자 없이 호출하면 일반 함수로서 호출된다.
2) 클래스는 상속을 지원하는 extends와 super 키워드를 제공하지만 생성자 함수는 지원X
3) 클래스는 호이스팅이 발생하지 않는것처럼 동작한다.
함수 선언문으로 정의된 생성자 함수는 함수 호이스팅이, 함수 표현식으로 정의한 생성자 함수는 변수 호이스팅이 발생한다.
4) 클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 strict mode를 해제할 수 없다.
하지만 생성자 함수는 strict mode가 지정되지 않는다.
5) 클래스의 contstructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumberable]]의 값이 false다.
다시 말해 열거되지 않는다.


클래스 정의

  • 클래스는 class 키워드를 사용하여 정의한다.
  • 일반적이지 않지만 함수와 마찬가지로 표현식으로 클래스를 정의할 수도 있음.
    함수와 마찬가지로 이름을 가질수도, 갖지 않을수도 있음
    const Person = class {}; // 익명 클래스 표현식
    const Person = class MyClass{}; // 기명 클래스 표현식
    

    클래스를 표현식으로 정의할 수 있다는 것은 클래스가 값으로 사용될 수 있는 일급객체라는 것을 의미.
    즉, 클래스는 일급 객체로서 다음과 같은 특징을 갖는다.

    • 무명의 리터럴로 생성가능 -> 런타임에 생성가능함
    • 변수나 자료구조(객체, 배열 등)에 저장 가능
    • 함수의 매개변수에게 전달가능
    • 함수의 반환값으로 사용가능
  • 클래스 몸체에서 정의할 수 있는 메서드는 constructor(생성자), 프로터타입 메서드, 정적 메서드 3가지가 있음.
    class Person {
      // 생성자
      constructor(name) {
        this.name = name; // name 프로퍼티는 public하다.  
      }
      // 프로터타입 메서드
      getName() {
        return this.name;
      }
      // 정적 메서드
      static sayHello() {
        console.log('Hello!');
      }
    }
      
    // 인스턴스 생성
    const person1 = new Person('Jung');
    // 인스턴스의 프로퍼티 참조
    console.log(person1.name); // Jung
    // 프로터타입 메서드 호출
    console.log(person1.getName()); // Jung
    // 정적 메서드 호출
    person1.sayHello(); // Hello !
    

인스턴스 생성

  • 클래스는 생성자 함수이며 new 연산자와 함꼐 호출되어 인스턴스를 생성한다.
    class Person {}
      
    // 인스턴스 생성
    const person1 = new Person();
    console.log(person1);   // Person {}
    
  • 함수는 new 연산자의 사용여부에 따라 일반 함수로 호출되거나 인스턴스 생성을 위한 생성자 함수로 호출되지만 클래스는 인스턴스를 생성하는 것이 유일한 존재이유이므로 반드시 new연산자와 함꼐 호출해야 한다.

메서드

  • 클래스 몸체에는 0개 이상의 메서드만 선언할 수 있다.
  • 클래스 몸체에서 정의가능한 메서드는 생성자(constructor), 프로토타입 메서드, 정적 메서드 3가지가 있다.

생성자contstructor)

  • 인스턴스를 생성하고 초기화하기 위한 특수한 메서드
  • constructor은 이름을 변경할 수 없다.
    class Person {
      constructor(name) {
        this.name = name; // 인스턴스 생성 및 초기화
      }
    }
    
  • constructor는 클래스내에 최대 한개만 존재가능함.
    만약 클래스가 2개이상의 constructor을 포함하면 문법에러가 발생!
  • constructor은 생략가능
    constructor을 생략하면 클래스에 빈 constructor가 암묵적으로 정의됨.
    constructor을 생략한 클래스는 빈 constructor에 의해 빈 객체를 생성함.
  • constructor은 별도의 반환문을 갖지않아야함.
    new 연산자와 함께 클래스가 호출되면 생성자 함수와 동일하게 암묵적으로 this, 즉 인스턴스를 반환하기 때문!
    만약 this가 아닌 다른 객체를 명시적으로 반환하면 this, 즉 인스턴스가 반환되지 못하고 return 문에 명시한 객체가 반환됨.
    하지만 명시적으로 원시값을 반환하면 원시값을 무시되고 암묵적으로 this가 반환됨.

프로토타입 메서드

  • 클래스 몸체에 정의한 메서드는 생성자 함수에 의한 객체생성방식과는 다르게 prototype 프로퍼티에 메서드를 추가하지 않아도 기본적으로 프로토타입 메서드가 된다.
    // 생성자 함수
    function Person(name) {
      this.name = name;
    }
      
    // 프로토타입 메서드  
    Person.prototype.getName = function() {
        return this.name;
    }
      
    // 클래스
    class Person {
      constructor(name) {
        this.name = name;
      }
      // 프로토타입 메서드
      getName() {
        return this.name;
      }
    }
    
  • 생성자 함수와 마찬가지로 클래스가 생성한 인스턴스는 프로토타입 체인의 일원이 된다.

정적 메서드

  • 정적메서드는 인스턴스를 생성하지 않아도 호출할 수 있는 메서드를 말한다.
  • 생성자 함수의 경우 정적 메서들르 생성하기 위해 다음과 같이 명시적으로 생성자 함수에 메서드를 추가해야 한다.
    function Person(name) {
      this.name = name;
    }  
    Person.sayHi = function() { // 정적 메서드
      console.log('Hi!');
    };
    
  • 클래스에서는 메서드에 static 키워드를 붙이면 정적 메서드(클래스 메서드)가 된다.
    class Person {
      static sayHi() { // 정적 메서드
        console.log('Hi!');
      }    
    }
    
  • 정적 메서드는 클래스 정의 이후 인스턴스를 생성하지 않아도 호출가능하다.
    정적 메서드는 프로토타입 메서드처럼 인스턴스로 호출하지 않고 클래스로 호출한다.
    Person.sayHi();  // Hi!
    
  • 정적 메서드는 인스턴스로 호출 불가능하다.
    정적 메서드가 바인딩된 클래스는 인스턴스의 프로토타입 체인상에 존재하지 않기 떄문!
    (인스턴스로 클래스의 메서드를 상속받을 수 없음)

정적 메서드와 프로토타입 메서드의 차이

  • 정적 메서드와 프로토타입 메서드는 자신이 속해있는 프로토타입 체인이 다르다.
  • 정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다.
  • 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다.

클래스에서 정의한 메서드 특징

  • function 키워드를 생략한 메서드 축약 표현 사용
  • 객체 리터럴과 다르게 클래스 메서드를 정의할 때는 콤마가 필요없음
  • 암묵적으로 strict mode로 실행
  • 내부 메서드 [[Constructor]]를 갖지 않는 non-constructor다.
    따라서 new 연산자와 함꼐 호출 할 수 없음.
  • for…in 문이나 Object.keys 메서드등으로 열거 할 수 없음.
    즉 프로퍼티 열거 가능 여부를 나타내며 불리언 값을 갖는 프로퍼티 어트리뷰트 [[Enumberable]] 값이 false다.

참조(모던 자바스크립트 Deep Dive)