클래스

우선 들어가기 전, 자바스크립트는 프로토타입 기반 객체 지향 언어이다. 모든 객체는 각 부모 역할을 담당하는 객체와 prototype으로 연결이 되어 있다. 때문에 class가 없어도 객체 지향 프로그래밍이 가능하다.


🙄 그럼 왜 써요,,?

class가 분명히 제공해주는 부분이 있다. 기존에 사용했던 prototype 기반의 상속 보다 class는 명료하게 구현되고, 객체를 생성하고 상속을 다루는데 있어 조금 더 명확한 문법들을 제공해준다.

❗ 하지만 자바스크립트의 class는 새로운 객체지향 상속 모델이 추가된 게 아니라 prototype 기반과 연결되어 있으며, class도 하나의 함수이며, 기존 prototype 기반 패턴을 class 기반 패턴처럼 사용할 수 있도록 하는 문법적 설탕이라 볼 수도 있다.


클래스 vs 생성자 함수

둘다 prototype 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지 않는다. 클래스틑 생성자 함수보다 엄격하며, 생성자 함수에서 제공하지 않는 기능도 제공한다.

클래스생성자 함수
new 연산자 미 선언에러 발생일반 함수로서 호출
extends, superOX
호이스팅발생하지 않는 것처럼 동작함수 선언문: 함수 호이스팅
함수 표현식 : 변수 호이스팅
엄격모드OX

프로토타입 객체지향을 구현했다는 점에서 유사하지만, 클래스틑 생성자 함수 기반의 객체 생성 방식보다 견고하고 명료하다. (extends, super 키워드는 상속 관계 구현을 더욱 간결하고 명료하게 한다.)

따라서, 자바스크립트에서의 클래스는 새로운 객체 생성 메커니즘 이라고 생각할 수 있다.


클래스 정의

  • class 키워드를 사용하여 정의한다.
  • class 이름은 생성자 함수와 마찬가지로 파스칼 케이스를 사용한다. (사용 안해도 에러는 안남)
  • 표현식으로도 정의 가능하나 일반적으론 사용 안함
  • class 몸체에 0개 이상의 메서드만 정의 가능. (생성자, 프로토타입 메서드, 정적 메서드)
// 클래스 선언
class Person {}

// 익명 클래스 표현식
const Person = class {};

// 클래스 선언문
class Person {
  	// 생성자
	constructor(name) {
    	// 인스턴스 생성 및 초기화
      this.name = name;
    }
  	// 프로토타입 메소드
  	sayHi() {
      console.log(`Hi name is ${this.name}`);
    }
  
  	// 정적 메소드
  	static sayHello() {
    	console.log('hello');
    }
}

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

// 프로퍼티 참조
console.log(me.name); // Lee

// 프로토타입 메소드 호출
me.sayHi(); // Hi name is Lee

// 정적 메소드 호출
Person.sayHello();	// hello

클래스 호이스팅

선언문으로 정의한 클래스는 함수 선언문과 같이 런타임 이전에 평가되어 함수 객체를 생성한다. 클래스는 호이스팅이 발생하지 않는 것처럼 보이나 그렇지 않다.

const Person = '';

{
  // 호이스팅이 발생하지 않으면 ''출력
  console.log(Person);	// 에러
  
  // 클래스 선언문
  class Person {}
}

클래스 선언문도 변수 선언, 함수 정의처럼 호이스팅이 발생한다. 단 클래스는 변수 선언처럼 호이스팅이 된다. 일시적인 사각지대에 빠지기 때문에 호이스팅이 발생하지 않는 것처럼 동작한다.

따라서 var, let, const, function, class 선언된 모든 식별자는 런타임 이전에 먼저 실행되기 때문에 호이스팅이 일어난다고 알아두기


인스턴스 생성

클래스는 생성자 함수이며 new 연산자와 함께 호출되어 인스턴스를 생성한다. new 연산자 사용 여부에 따라 일반 함수 / 생성자 함수로 호출되는데, 클래스는 인스턴스 생성을 위해 만들어진 이유도 있기 때문에 반드시 new 연산자와 호출한다.

  • 표현식으로 정의된 클래스의 경우, 식별자를 사용해 인스턴스를 생성하지 않고 기명 클래스 표현식의 클래스 이름을 사용하여 인스턴스를 생성하면 에러가 난다.
const Person = class MyClass {};

// 클래스를 가르키는 식별자로 인스턴스 생성해야 함.
const me = new Person();

// MyClass는 함수와 동일하게 클래스 몸체 내부에서만 유효한 식별자
console.log(MyClass);		// 에러

const you = new MyClass();	// 에러

📌 extends

extends 는 클래스의 자식 클래스를 생성할때 사용한다. 상속을 통해 클래스를 확장하려면 extends 키워드를 사용하여 상속받을 클래스를 정의한다.

// 생성자 함수로 선언된 수퍼(베이스/부모) 클래스
class Base(a) {
  this.a = a;
}

// 상속받은 서브(파생/자식) 클래스
class Derived extends Base {}

const derived = new Derived(1);
console.log(derived);	// Derived {a: 1}

📌 super

super 은 부모 생성자를 호출한다. 함수처럼 호출할 수도 있고 this와 같이 식별자처럼 참조할 수 있는 특수한 키워드이다.

  • super를 참조하면 부모 클래스의 메서드를 호출할 수 있으며, 부모 클래스의 constructor를 호출한다.
  • 부모 생성자가 해결하지 못한 부분은 super를 호출하여 자식 생성자가 하도록 만든다.
// 수퍼(베이스/부모) 클래스
class Base {
  constructor(a,b) {
  	this.a = a;
    this.b = b;
  }
}

// 서브(파생/자식) 클래스
class Derived extends Base {
	// 암묵적으로 constructor가 정의된다.
  	constructor(a, b, c) {
    	super(a, b);
      this.c = c;
    }
}

const derived = new Derived(1, 2, 3);
console.log(derived);	// Derived {a: 1, b: 2, c: 3}
  • 서브 클래스에서 constructor를 생략하지 않는 경우, 서브 클래스 constructor에서 반드시 super를 호출해야 한다.
  • 서브 클래스의 constructor에서 super를 호출하기 전에는 this를 참조할 수 없다.
  • super는 반드시 서브 클래스 constructor에서만 호출함. 그외에 호출은 에러

❗ 상속 클래스 인스턴스 생성 과정

상속 관계의 클래스가 인스턴스를 생성하는 과정을 보며 super의 사용법 익혀보쟈!

// 수퍼 클래스
class Rectangle {
  constructor(width, height) {
  	this.width = width;
    this.height = height;
  }
}

getArea() {
	return this.width * this.height;
}

toString() {
	return `whidth = ${this.whidth}, heigth = ${this.height}`
}

// 서브 클래스
class ColorRectangle extends Rectangle {
  	constructor(width, height, color) {
    	super(width, height);
      this.color = color;
    }
  
  	// 메서드 오버라이딩
  	toString() {
    	return super.toString() + `, color = ${this.color}`
    }
}

const colorRectangle = new ColorRectangle(2, 4, 'red');
console.log(colorRectangle);  // ColorRectangle {width: 2, height: 4, color: 'red'}

// 상속을 통해 getArea 메서드 호출
console.log(colorRectangle.getArea());  // 8
// 오버라이딩된 toString 메서드 호출
console.log(colorRectangle.toString()); // width = 2, height = 4, color = red
  1. 서브 클래스의 super 호출
  2. 수퍼 클래스의 인스턴스 생성과 this 바인딩
  3. 수퍼 클래스의 인스턴스 초기화 👉 constructor가 실행되어 this 바인딩 인스턴스 초기화
  4. 서브 클래스 constructor로의 복귀와 this 바인딩 👉 super가 반환한 인스턴스가 this에 바인딩
  5. 서브 클래스의 인스턴스 초기화 👉 super 호출 후, constructor안의 인스턴스 초기화
  6. 인스턴스 반환 👉 모든 처리가 끝난 후 인스턴스 반인딩 된 this 반환

👀 그래서 결론은?

  • 클래스는 함수 선언 호이스팅이 일어나지 않기 때문에 선언 전에도 함수 사용이 가능하게 됨 그래서 호이스팅으로 인해 코드가 꼬일 수 있는 단점 해결!
  • 자동으로 strict모드를 적용시키기 때문에 좀 더 꼼꼼한 코딩이 가능해진다.

class가 생성자 함수를 완벽히 대체한다고 보기 힘들지만, 꼼꼼한 코드를 위해 class를 지향해보쟈

profile
발로하는 코딩 정리기

0개의 댓글