JavaScript | 클래스(Class)

Kate Jung·2022년 3월 1일
0

JavaScript

목록 보기
31/39
post-thumbnail
  • ES6에 추가된 스펙

📌 관련 기본 개념 및 용어 정리

  • JS에서 클래스는 함수의 한 종류
  • 연관 있는 변수와 함수로 구성
  • class 라는 키워드를 사용.

🔹 용어

1. 인스턴스

  • 의미 : 객체
  • ex) 붕어빵 틀 = 클래스 , 붕어빵 = 인스턴스(또는 객체)

2. 프로퍼티

  • 의미 : 클래스 내부에 만드는(포장하는) 변수
  • 별칭 : 멤버변수
  • 객체 내부에서 사용하는 정보, 객체 내부 함수(메서드)에서 처리한 결과값 저장

3. 메서드

  • 의미 : 클래스에 만드는(포장하는) 함수

  • 별칭 : 멤버함수

  • 주로 객체 프로퍼티 값 변경 및 탐색, 클래스 대표 기능 등이 담김

  • 사용법

    클래스명.메소드명()

  • 저장 위치

    해당 클래스의 프로토타입 ( ↔ 생성자 함수의 저장 위치: 객체 내부 )

4. 생성자 (constructor)

  • 객체를 만들어주는 생성자 메서드
  • 인스턴스가 만들어지면서 자동으로 호출되는 함수
  • 주 용도: 프로퍼티 초기화 (내부에서 객체 초기 값 정해짐.)
  • 위치: class 내부
  • new를 통해 호출 시, 자동 실행.
  • 인수 받기 가능.

📌 생성자 함수와 비교

🔹 공통점

  • 모두 비슷한 형태의 객체를 생성하기 위한 방법
    // 👉 생성자 함수
    const User  = function(name, age){
      this.name = name;
      this.age = age;
      this.showName = function(){
        console.log(this.name)
      }
    }
    
    const mike = new User('mike',30)
    
    // 👉 class
    class User2 {
      constructor(name, age){ 
        this.name = name; // 이렇게 하면 객체에 name, age가 만들어짐
        this.age = age;
      }
      showName () {
        console.log(this.name)
      }
    }
    
    const tom = new User2('tom', 19)
  • new를 통해서 호출 시, 내부에서 정의된 내용으로 객체 생성
    mike 
    // User {name: 'mike', age: 30, showName: ƒ}
    tom 
    // User2 {name: 'tom', age: 19}
  • 함수 호출법
    // 동일
    mike.showName() // mike
    tom.showName() // tom

🔹 생성자 함수: 클래스처럼 동작하도록 제작 가능

const User  = function(name, age){
  this.name = name;
  this.age = age; // 👈 showName() 제거
}

User.prototype.showName = function(){ // 👈 showName() 추가
  console.log(this.name)
}

const mike = new User('mike',30)
  • 확인

    같음. 생성자 함수로도 구현 가능


🔹 차이점

  • class가 단순히 문법의 편의성을 위해 탄생 x

◽ 에러 파악 (new 없이 실행 시)

  • class: 가능 (new없이 호출 시 에러가 발생되도록 설계)
  • 생성자 함수: 불가 (잘 동작)
  • 예시
    1. 생성자 함수: 클래스처럼 동작하도록 제작

    2. new 제거

      // 👉 생성자 함수
      const User  = function(name, age){
        this.name = name;
        this.age = age;
      }
      
      User.prototype.showName = function(){
        console.log(this.name)
      }
      
      const mike = User('mike', 30) // new 제거
      
      // 👉 class
      class User2 {
        constructor(name, age){ 
          this.name = name;
          this.age = age;
        }
        showName () {
          console.log(this.name)
        }
      }
      
      const tom = User2('tom', 19) // new 제거
    3. 확인

      
      mike 
      /*
      - 결과: undefined
      - 설명: 문제 없음. User함수가 return문이 없기 때문에 반환 x -> undefined가 됨. 그 값이 mike로 들어옴.
      - 문제: 실수한 코드이지만 잘 동작. -> 에러 즉시 알아 수 없음. */ 
      
      tom
      /* 
      - 결과: TypeError 발생.
      - 에러 메세지: TypeError: Class constructor User2 cannot be invoked without 'new'(class는 new 없이 실행할 수 없다.)*/
    4. new를 다시 넣고 콘솔창 비교

      class(User2)의 constructor(프로토타입 내부): class 명시.

      constructorclass인 것 파악 가능 & 이 경우, new없이 호출 시 에러가 발생되도록 설계됨.

◽ for in 문

  • class: 객체가 가지고 있는 프로퍼티만 보임
  • 생성자 함수: 프로토타입에 포함된 프로퍼티들을 다 보여줌.
    (객체가 가지고 있는 프로퍼티 판별 방법: hasOwnProperty 사용)
  • 예시
    // ✅ 생성자 함수
    for (const p in mike) {
      console.log(p)
    }
    /*
    - 결과:
      name
      age
      showName
    - 설명: 다 나옴(프로토타입에 있는 것 까지)
    */
    
    // ✅ class
    for(const p in tom){
      console.log(p)
    }
    /*
    - 결과:
      name
      age */

📌 상속

🔹 구현 방법

  • 클래스: extends 사용

  • 생성자 함수: 프로토타입 이용

🔹 예시 코드

class Car {
  constructor(color){
    this.color = color;
    this.wheels = 4;
  }
  drive(){
    console.log('drive...')
  }
  stop(){
    console.log('STOP!')
  }
}

class Bmw extends Car {
  park(){
    console.log('park')
  }
}

const z4 = new Bmw('blue')

z4 확인

  • Car클래스에서 선언한 color, wheels가 들어있음

  • 1번째 프로토타입

    park

    (class내부에서 선언한 메소드 위치: 프로토 타입 밑)

  • 2번째 프로토 타입

    drive, stop

z4.drive() 실행

  • 결과

    'drive...’

  • 과정

    1. z4객체에서 찾음. → 없음.
    2. 1번째 프로토타입에서 찾음. → 없음.
    3. 2번째 프로토타입에서 찾음. → 나옴.
    4. drive() 실행

📌 오버라이딩

오버라이딩 이란?

  • JS 객체의 상속받은 부모의 메소드 재정의
  • super.메소드명으로 부모 클래스에 정의된 메소드를 사용하는 방식

🔹 메소드 오버라이딩(method overriding)

  • 부모 & 자식 클래스의 메소드명이 서로 같다면 → 덮어쓰게 됨.

    부모의 메소드를 사용 & 확장하고 싶다면 super (super.메소드명) 사용

  • 예시

    • 문제

      부모(Car) & 자식(Bmw) 클래스 간 동일한 이름의 메소드 존재

      class Car {
        constructor(color){
          this.color = color;
          this.wheels = 4;
        }
        drive(){
          console.log('drive...')
        }
        stop(){ // 👈 동일
          console.log('STOP!')
        }
      }
      
      class Bmw extends Car {
        park(){
          console.log('park')
        }
        stop(){ // 👈 동일
          console.log('OFF')
        }
      }
      
      const z4 = new Bmw('blue')
      
      z4.stop() // 'OFF' 👈 덮어 쓰게 됨.
    • 해결

      오버라이딩 활용

      class Car {
        (...생략)
        stop(){
          console.log('STOP!')
        }
      }
      
      class Bmw extends Car {
        (...생략)
        stop(){
          super.stop(); // 👈 Car의 stop() 사용
          console.log('OFF')
        }
      }
      
      const z4 = new Bmw('blue')
      
      z4.stop() 
      /*
      - 결과:
      STOP! 👈 부모 클래스(Car)의 메소드(stop()) 먼저 실행
      OFF */

🔹 생성자 오버라이딩(constructor overriding)

  • 자식 생성자는 무조건 부모 생성자를 호출해야 됨.
  • constructor에서 this 사용 전, 부모 생성자(super constructor) 선 호출 필수
  • 자식 classconstructor에도 동일한 인수를 받는 작업 필요

◽ 선호출 이유

classconstructor는 빈 객체 ({})를 만들어주고 this로 이 객체를 가리키는 작업을 함.

반면 extends를 써서 만드는 자식 class는 위 작업을 건너뜀.

→ 그래서 항상 super() 키워드로 부모 classconstructor를 실행해야 함.

◽ 예시

  1. 문제

    class Car {
      constructor(color){
        this.color = color;
        this.wheels = 4;
      }
      drive(){
        console.log('drive..')
      }
      stop(){
        console.log('STOP!')
      }
    }
    
    class Bmw extends Car {
      constructor(){
        this.navigation = 1; // 👈[에러 발생 원인] 부모 생성자(super constructor) 無
      }
      park(){
        console.log('PARK')
      }
    }
    
    const z4 = new Bmw('blue')
  2. 해결 1 | 부모 생성자 선호출

    class Car {
      constructor(color){  // {} 👈 빈 객체
        this.color = color;
        this.wheels = 4;
      }
      (...생략)
    }
    
    class Bmw extends Car {
      constructor(){
        super(); // 👈 부모 class의 constructor 실행 
        this.navigation = 1;
      }
      (...생략)
    }
    
    const z4 = new Bmw('blue')
    
    // 확인
    z4
    
    /*
    - 결과: 
    Bmw {color: undefined, wheels: 4, navigation: 1}
    	color: undefined
    	navigation: 1
    	wheels: 4
    	[[Prototype]]: Car 
    
    - 해석: 
    color는 undefined.(생성 시, 'blue'넣었음에도 불구)
    navigation는 잘 들어옴.
    
    - 해결 방법:
    제대로 동작하기 위해서는 자식 class의 constructor에 동일한
    인수를 받는 작업을 해야 함. */
  3. 해결 2 | 자식 classconstructor에 동일 인수 넘김

    (...생략)
    
    class Bmw extends Car {
      constructor(color){ // 👈 color 받아 넘김
        super(color); // 👈
        this.navigation = 1;
      }
      (...생략)
    }
    
    (...생략)
    
    // 확인
    z4
    
    /*
    - 결과:
    Bmw {color: 'blue', wheels: 4, navigation: 1}
    	color: "blue"
    	navigation: 1
    	wheels: 4
    	[[Prototype]]: Car 
    
    - 해석:
    color: 'blue'까지 잘 나옴.
    */
  4. 자식 classconstructor가 없을 경우

    자식 생성자는 무조건 부모 생성자를 호출

    class Car {
      constructor(color){
        this.color = color;
        this.wheels = 4;
      }
      (<...생략)
    }
    
    class Bmw extends Car {
      constructor(...args){ // 👈 constructor가 없으면 이 부분이 있는 것처럼 행동.
        super(...args);
      }
      (...생략)
    }
    
    const z4 = new Bmw('blue')
  5. 자식 classconstructor가 있을 경우

    • 없을 경우처럼 처리되지 않음.

      → 항상 super를 이용해 호출 & this.property로 할당 필요

    class Car {
      constructor(color){ // 3. 이 작업 제대로 실행 가능.
        this.color = color;
        this.wheels = 4;
      }
      (...생략)
    }
    
    class Bmw extends Car {
      constructor(){ // 1. color 값을 여기서 받은 다음
        super(); // 2. 이쪽으로 넘겨줘야 
      }
      (...생략)
    }
    
    const z4 = new Bmw('blue')

참고

  • 코딩앙마_자바스크립트 중급
profile
복습 목적 블로그 입니다.

0개의 댓글