[모던 자바스크립트 Deep dive] Study - 25.클래스

n-u·2022년 5월 12일
0
post-thumbnail

25. 클래스

25.1 클래스는 프로토타입의 문법적 설탕인가?

자바스크립트는 프로토타입 기반 객체지향 언어이다. 하지만 다른 객체지향 언어와 차이점에 있어 혼란이 있어왔다.

ES6에 도입된 클래스는 기존 프로토타입 기반 객체지향 프로그래밍보다 자바, c#과 같은 클래스 객체 지향 프로그래밍 모델과 유사한 메커니즘을 갖게 되었다.

클래스는 함수이며 기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용할 수 있도록 하는 문법적 설탕이라고 볼 수 있다.

단, 클래스틑 생성자 함수보다 엄격하며 생성자함수가 제공하지 않는 기능도 제공한다.

클래스 vs 생성자 함수

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

생성자 함수와 클래스는 프로토타입 기반의 객체지향을 구현했다는 점에서 매우 유사하지만, 간결하고 명료하게 코드들의 관계를 구현하는 차이점이 있다.

클래스는 생성자함수의 문법적 설탕이라기 보다는 "새로운 객체 생성 매커니즘"으로 보는 것이 합당하다.




25.2 클래스의 정의

  1. class 키워드를 사용하여 정의한다.
  class Person{}
  1. 표현식으로 클래스르르 정의 할 수도 있다.
    (익명/기명 둘다 가능하다)
//익명 클래스 표현식
const Person = class {};
//기명 클래스 표현식
const Person = class MyClass{};
  • 파스칼 케이스를 사용하는 것이 일반적이다.
  • 클래스는 함수이며, 값처럼 사용할 수 있는일급 객체이다.

    일급 객체의 특징

    1. 무명 리터럴로 생성할 수 있다.(런타임에 생성이 가능)
    2. 변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
    3. 함수의 매개변수에게 전달할 수 있다.
    4. 함수의 반환값으로 사용할 수 있다.
  • 클래스 몸체에는 0개 이상의 메서드만 정의 할 수 있다.
    • 정의 할 수 있는 메서드 : construnctor(생성자), 프로토타입 메서드, 정적 메서드
//클래스 선언문
class Person{
//생성자
  construnctor(name){
    //인스턴스 생성 및 초기화
    this.name = name;
  }
  //프로토타입 메서드
  sayHi(){
    console.log(`Hi! My name is ${this.name}`);
  }
  //정적 메서드
  static sayHello(){
    console.log('Hello!');
  }
}

  //인스턴스 생성 
  const me = new Person('Lee');

  //인스턴스의 프로퍼티 참조
  console.log(me.name);	//lee
  //프로토타입 메서드 호출
  me.sayHi();	//Hi! My name is Lee
  //정적메서드 호출
  Person.sayHello();	//Hello!



25.3 클래스 호이스팅

클래스는 클래스 정의 이전에는 참조할 수 없다.

클래스 선언문도 변수 선언, 함수 정의와 마찬가지로 호이스팅이 발생한다. 하지만, 클래스는 let, const 키워드로 선언한 변수 처럼 호이스팅이 된다.

따라서 선언문 이전에 일시적 사각지대에 빠지게 되어 호이스팅이 발생하지 않는 것처럼 동작한다.

  • let, const 키워드 호이스팅 : 런타임 이전에 먼저 실행되지만 초기값을 할당하지 않은 상태이다.



25.4 인스턴스 생성

클래스는 생성자 함수이며, new 연산자와 함께 생성되어 인스턴스를 생성한다.

class Person{}
//인스턴스 생성
const me = new Person();
console.log(me);	//Person{}
  • 반드시 new 연산자와 함께 호출해야 한다.
    => 클래스는 인스턴스를 생성하는 것이 유일한 존재 이유이기 때문에
  • new 키워드없이 호출하면 타입에러가 발생한다.
  • 클래스 표현식으로 정의된 클래스 경우, 클래스를 가리키는 식별자를 사용해 인스턴스를 생성해야 한다.
const Person = class MyClass{};

//함수 표현식과 마찬가지로 클래스를 가리키는 식별자로 인스턴스를 생성해야 한다.
const me = new Person();

//클래스 이름 MyClass는 함수와 동일하게 클래스 몸체 내부에만 유효한 식별자이다.
console.log(MyClass);
//ReferenceError : MyClass is not defined

const you = new MyClass();
//ReferenceError : MyClass is not defined



25.5 메서드

클래스 몸체에는 0개이상의 메서드만 선언할 수 있다.

  • 클래스 몸체에서 정의할 수 있는 메서드
    1. constructor(생성자)
    2. 프로토타입 메서드
    3. 정적 메서드

1. constructor

  • 인스턴스를 생성하고 초기화하기 위한 특수한 메서드
  • 이름을 변경할 수 없다.

constructor는 메서드로 해석이 되는 것이 아닌, 클래스가 평가되어 생성한 함수 객체 코드 일부이다. 즉 클래스 내부의 constructor는 함수 객체라고 할 수 있다???

  • constructor 내부에서 this에 추가한 프로퍼티는 인스턴스 프로퍼티가 된다. 즉 클래스가 생성한 인스턴스를 가리킨다.

특징

  1. constructor는 클래스 내에 최대 한개만 존재
  2. constructor는 생략 할 수 있다.
    • 생략하면 빈 constructor가 암묵적으로 정의된다. 즉 빈 객체를 생성한다.
  3. constructor는 별도의 반환문을 갖지 않아야 한다.(그냥 return을 쓰지 않는 것이 바람직하다)
    • this가 아닌 다른 객체를 명시적으로 반환하면 this가 가리키는 인스턴스가 반환되지 못하고 return 문에 명시된 객체가 반환된다.
    • but)) return이 명시한 값이 원시값이라면 무시가 되고 암묵적으로 this(인스턴스)를 반환한다.

인스턴스 생성, 프로퍼티 추가, 초기화

  1. 초기화된 인스턴스를 생성하려면, constuctor내부의 this에 인스턴스 프로퍼티를 추가한다.

    class Person{
      constructor(){}
    }

    빈 객체를 가진 클래스에 this를 이용해 인스턴스 프로퍼티를 추가해보자

    class Person{
        constructor(){
        this.name = "lee";
        this.address = "Seoul";
        }
      }
  2. 초기값은 constructor의 매개변수에게 전달한다.

    • 인스턴스를 생성할때 클래스 외부에서 인스턴스 프로퍼티의 초기값을 전달하려면 constructor의 매개변수를 선언하고 인스턴스를 생성할 때 초기값으르 전달한다.
      class Person{
          constructor(name, address){
          this.name = name;
          this.address = address;
          }
        }
      //인수로 초기값을 전달한다. 초기값은 constructor에 전달된다.
      const me = new.Person('Lee', 'Seoul');
      console.log(me);
      //Person{name : 'Lee', address : 'Seoul'}

프로토타입 메서드

생성자 함수와 다르게 프로토타입 메서드를 사용하지 않아도 기본적으로 프로토메서드 타입이 된다.

class Person{
  //생성자
  constructor(name){
    //인스턴스 생성 및 초기화
    this.name = name;
  }
  
  //프로토타입 메서드
  sayHi(){
    console.log(`Hi! My name is ${this.name}`);
  }
}

const me = new Person('Lee');
me.sayHi();
//Hi! My name is Lee
  • 클래스가 생성한 인스턴스는 프로토타입 체인의 일원이 된다.
  • 프로토타입 메서드는 프로토타입 객체로 바인딩된다.
  • 인스턴스는 프로토타입 메서드를 상속받아 사용 가능하다.

정적 메서드

인스턴스를 생성하지 않아도 호출할 수 있는 메서드

  • static 키워드를 붙이면 정적 메서드(클래스 메서드)가 된다.
class Person{
  //생성자
  constructor(name){
    //인스턴스 생성 및 초기화
    this.name = name;
  }
  
  //정적 메서드
  static sayHi(){
    consoel.log('Hi');
  }
}
  • 클래스에 바인딩 된다.
  • 클래스는 클래스가 정의가 되는 평가 시점에 함수 객체가 되므로 인스턴스와 달리 별다른 생성과정이 필요가 없다.
  • 따라서 인스턴스로 호출 할 수 없다.(클래스 정의 이후 인스턴스를 생성하지 않아도 호출할 수 있다)
  • 인스턴스로 호출하지 않고 클래스로 호출한다.
//인스턴스 생성
const me = new Person('Lee');
// 정적 메서드는 클래스로 호출한다.
Person.sayHi();
//인스턴스를 이용해 호출하게 되면 에러가 나온다.
me.sayHi();
//TypeError : me.sayHi is not a function

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

  1. 정적 메서드와 프로토타입 메서드는 자신이 속해 있는 프로토타입 체인이 다르다.
  2. 정적 메서드는 클래스로 호출하고, 프로토타입 메서드는 인스턴스로 호출한다.
  3. 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만, 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다.
  4. 각 가지고 있는 this는 다른 객체를 가리킨다.
    정적 메서드는 클래스를 프로토타입 메서드는 인스턴스(자신을 호출한 객체)를 가리킨다.

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

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







24일차를 마치며

어제는 갑작스러운 약속때문에 하지 못해 또 밀려버렸다. 점점 목표로 했던 40일기간에 완독하는 것과는 거리 멀어지고 있어 마음이 아프다. 어차피 많이 밀려 있기도 하니까 5월안에 완독으로 계획을 바꿔야 겠다. 이렇게 계획을 변경해야 조급하지도 않고 끝까지 해나갈 수 있을 것 같다는 생각이 드는데.. 5월 말이 될때 미래의 나는 어떻게 하고 있을지...그때는 6월 중순까지 완독할거야 이러고 잇는건 아닌지...ㅋ 그때 되면 그래도 마지막 부분을 공부하고 있기를....ㅋㅋ

이번 파트는 공부한 적이 없는 새로운 내용이여서 어려울 것같아 걱정했는데 걱정한 것 보다는 어렵지는 않다. 중간에 이해되지 않은 설명들이 있긴 했어도 전체적인 흐름을 이해가 되서 힘들지 않았다. 공부를 하면서 알 것 같아!하는 생각도 들고 이 챌린지를 하면서 공부한 내용이 나올때면 좀더 집중하면서 공부 한 것 같다. 이 챌린지가 나에게 무모하고 수준에 맞지 않는 공부 법이었나 하는 생각을 했었다. 근데 오늘은 책 내용이 완전하지는 않더라고 내용의 큰 흐름이 이해가 되서 지금까지 공부한 것이 무의미하지 않은 것 같아 다행이다.

profile
기록하며 발전하는 삶

0개의 댓글