클래스(Class)

BaeSeong-min·2025년 1월 2일
0

ES6 문법공부

목록 보기
15/22
post-thumbnail

📙ES6 문법공부


📌클래스

생성자 함수 말고도 클래스를 사용해서 객체를 생성할 수 있다. 클래스는 ES6에 추가된 문법이다.

✨클래스를 만들고, 클래스의 인스턴스 생성하는 방법

class User {
   constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  showName() {
    console.log(this.name);
  }
}

const tom = new User("Tom", 19);

console.log(tom.showName()); // "Tom"

class라는 키워드를 사용해서 클래스를 만든다. 클래스 내부에는 객체를 만들어주는 constructor() 생성자 메서드가 있다. new를 통해 클래스를 호출하면 constructor() 메서드를 자동으로 실행한다. constructor() 메서드는 인수를 넘겨받아, 생성된 객체의 속성을 초기화할 수 있다. 클래스 내부에 정의된 메서드는 클래스의 프로토타입 객체에 저장된다.

✏️클래스는 함수 객체이다

자바스크립트에서 클래스는 함수 객체이다.

class User = {};
console.log(typeof User); // "function"

클래스는 내부적으로 함수로 구현된다. 실제로 typeof 연산자를 사용하면 클래스가 함수임을 확인할 수 있다.

하지만, 클래스는 함수와 달리 호이스팅되지 않기 때문에 선언 전에 사용할 수 없다. 그리고, 클래스는 반드시 new 키워드로 호출해야 한다.

클래스와 생성자 함수는 모두 프로토타입에 메서드가 저장된다.

💡new 연산자를 붙였을 때 내부 알고리즘 동작 방식

생성자 함수와 클래스를 호출할 때, new 연산자를 붙이면 this라는 빈 객체를 생성하고 반환하는 알고리즘이 내부적으로 동작한다. new 연산자 붙여서 생성자 함수를 호출하면 함수는 아래 코드와 같이 알고리즘이 내부적으로 동작한다. 생성자 함수 내부에 this 객체를 생성하고, this 객체를 반환한다.

const User = function (name, age) {
  // this = {};
  this.name = name;
  this.age = age;
  this.showName = function() {
    console.log(this.name);
  };
  // return this;
};

const mike = new User("Mike", 30);

new 연산자 붙여서 클래스를 호출하면 클래스는 아래 코드와 같이 알고리즘이 내부적으로 동작한다. constructor() 메서드 내부에 this라는 빈 객체를 만들고, this 객체를 반환한다.

class User {
   constructor(name, age) {
    // this = {};
    this.name = name;
    this.age = age;
    // return this; 
  }
  
  showName() {
    console.log(this.name);
  }
}

const tom = new User("Tom", 19);

💡new 연산자를 빼고 클래스를 호출하면?

생성자 함수는 new 없이 호출을 해도 오류가 나지 않는다. 생성자 함수 자체만 실행될 뿐이다. return문이 없다면 undefined를 반환한다.

반면, 클래스는 new 없이 호출할 수 없다. TypeError 오류를 일으킨다.

생성자 함수를 통해 생성된 객체의 경우 생성자 함수의 프로토타입 객체의 constructor 속성은 해당 객체를 생성한 생성자 함수를 가리키고 있다. 생성자 함수를 통해 생성된 객체의 경우 생성자가 함수인 것을 알 수 있다.

반면, 클래스를 통해 생성된 객체의 경우 생성자가 클래스인 것을 확인할 수 있다. 생성자가 클래스인 객체는 new 연산자를 반드시 붙여서 생성되도록 설계되어 있다.

✨클래스 인스턴스의 프로퍼티를 for..in 문으로 순회하는 예제

생성자 함수의 인스턴스를 for..in문으로 순회한 적이 있다. (for...in문을 이용한 객체 프로퍼티 순회 예제)

생성자 함수 인스턴스를 for..in문으로 순회하면, 프로토타입 객체에 있는 속성까지 순회한다. 객체가 직접 가진 프로퍼티와 프로토타입에 있는 프로퍼티를 구분하기 위해서 hasOwnProperty() 메서드를 사용한다. 하지만, 클래스 인스턴스를 for..in문으로 순회할 때는 프로토타입에 포함되는 클래스의 메서드가 for..in문에서 제외된다.

for(const p in mike) {
  console.log(p); // name, age, showName
}

for(const p in tom) {
  console.log(p); // name, age
}

📌클래스의 상속

클래스에서 상속은 extends 키워드를 사용한다. z4 인스턴스는 colorwheels 속성을 직접 가지고 있다. 메서드의 경우, z4 객체의 __proto__Bmw 클래스의 프로토타입을 가리킴으로써 상속 받는다. 그리고, Bmw 클래스의 프로토타입이 가진 속성 __proto__Car 클래스의 프로토타입을 가리킴으로써 상속 받는다.

z4.drive();를 실행했을 때 동작을 살펴보자. 먼저 z4 객체가 직접 가지고 있는 프로퍼티를 탐색한다. drive() 메서드가 없으니 부모 객체의 Bmw 클래스의 프로토타입에서 drive() 메서드를 탐색한다. 마찬가지로 drive() 메서드가 없으니, 부모 객체의 Car 클래스의 프로토타입에서 메서드를 탐색한다. 이때, drive() 메서드가 있으므로 접근하여 사용할 수 있다.

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.drive() // "drive.."

✨메서드 오버라이딩(method overriding)

동일한 이름으로 메서드를 정의하면 덮어 쓰게 된다. 상속 받는 자식 클래스의 메서드와 부모 클래스의 메서드 명이 동일하다면, 기본적으로 자식 클래스의 메서드로 덮어 쓰게 된다.

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"

만약, 부모 클래스의 메서드를 그대로 사용하면서 자식 클래스가 상속받고 싶다면 super 키워드를 사용하면 된다.

class Bmw extends Car {
  park() {
    console.log("PARK");
  }
  stop() {
    super.stop(); // STOP!
    console.log("OFF"); // OFF
  }
}

✨생성자 오버라이딩(overriding)

생성자 오버라이딩이란 자식 클래스에서 부모 클래스의 생성자 메서드를 재정의하여, 부모 클래스의 생성자 기능을 확장하거나 수정하는 과정이다. 생성자 메서드를 재정의할 때, super 키워드를 사용하여 부모 클래스의 constructor 메서드를 호출하고, constructor에 정의된 속성을 자신 클래스로 가져온다.

부모 클래스를 상속받는 자식 클래스의 constructor 생성자 메서드는 new 연산자를 붙여서 자식 클래스를 호출해도, this 빈 객체를 만드는 작업을 건너뛴다.

그리고, 부모 클래스의 constructor 메서드는 직접 상속되지 않는다. 애초에 부모 클래스의 프로토타입에는 constructor 메서드가 저장되지 않는다.

따라서, 자식 클래스의 constructor 생성자 메서드에서 this를 사용하기 전에 super 키워드로 부모 클래스의 생성자를 반드시 먼저 호출해야한다. super 키워드를 사용해서 부모 클래스의 생성자를 호출할 때, 필요한 인자를 넣어줘야 한다. super를 호출하기 전에 this 객체를 사용한다면 아직 this는 선언되지 않았기에 에러가 발생한다.

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

class Bmw extends Car {
  constructor(color) {
    super(color);
    this.navigation = 1;
  }
  park() {
    console.log("PARK");
  }
}

const z4 = new Bmw("blue");

console.log(z4.color); // "blue"

💡자식 클래스의 constructor 메서드를 정의하지 않은 경우

만약, 자식 클래스에서 constructor 생성자 메서드를 따로 정의하지 않으면 부모 클래스의 constructor 메서드가 호출된다.

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

class Bmw extends Car {
  /*
  constructor(...args) {
    super(...args)
  }
  */
  
  park() {
    console.log("PARK");
  }
}

const z4 = new Bmw("blue");
profile
성민의 개발 블로그 🔥

0개의 댓글