클래스

RHUK2·2021년 5월 26일
0

Javascript

목록 보기
53/56

📚 Reference


javascript.info, https://ko.javascript.info/class

참고 사이트에 내용을 개인적으로 복습하기 편하도록 재구성한 글입니다.
자세한 설명은 참고 사이트를 살펴보시기 바랍니다.


서론


클래스는 객체 지향 프로그래밍에서 특정 객체를 생성하기 위해 변수와 메서드를 정의하는 일종의 틀로, 객체를 정희하기 위한 상태와 메서드로 구성됩니다.

실무에서는 사용자나 물건같이 동일한 종류의 객체를 여러 개 생성해야 하는 경우가 잦습니다. 이럴 때 new 연산자와 생성자 함수를 이용해 동일한 객체를 쉽게 생성할 수 있습니다.

여기에 더하여 ES6에서 도입된 클래스라는 문법을 사용하면 객체 지향 프로그래밍에서 사용되는 다양한 기능을 자바스크립트에서도 사용 가능합니다.


기본 문법


클래스는 다음과 같은 기본 문법을 사용해 만들 수 있습니다.

class MyClass {
  // 여러 메서드를 정의할 수 있음
  constructor() { ... }
  method1() { ... }
  method2() { ... }
  method3() { ... }
  ...
}

이렇게 클래스를 만들고, new MyClass()를 호출하면 내부에서 정의한 메서드가 들어 있는 객체가 생성됩니다.

객체의 기본 상태를 설정해주는 생성자 메서드 constructor()new에 의해 자동으로 호출되므로, 특별한 절차 없이 객체를 초기화 할 수 있습니다.

class User {

  constructor(name) {
    this.name = name;
  }

  sayHi() {
    console.log(this.name);
  }

}

const user = new User('Tomas');
user.sayHi(); // Tomas 

new User('Tomas')를 호출하면 다음과 같은 일이 일어납니다.

  • 새로운 객체가 생성됩니다.

  • 넘겨받은 인수와 함께 constructor()가 자동으로 실행됩니다.

  • 인수 'Tomas'this.name에 할당됩니다.

  • 위 과정을 거친 후에 user.sayHi() 같은 객체 메서드를 호출할 수 있습니다.

메서드 사이엔 쉼표가 없습니다.

초보 개발자는 클래스 메서드 사이에 쉼표를 넣는 실수를 저지르곤 합니다. 이렇게 쉼표를 넣으면 문법 에러가 발생합니다.

클래스와 관련된 표기법은 객체 리터럴 표기법과 차이가 있습니다. 클래스에선 메서드 사이에 쉼표를 넣지 않아도 됩니다.


클래스란?


클래스는 자바스크립트에서 새롭게 창안한 개체가 아닙니다. 자바스크립트에서 클래스는 함수의 한 종류입니다.

class User {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    console.log(this.name);
  }
}

// 클래스는 함수입니다.
console.log(typeof User); // function

// 정확히는 생성자 메서드와 동일합니다.
console.log(User === User.prototype.constructor); // true

// 클래스 내부에서 정의한 메서드는 User.prototype에 저장됩니다.
console.log(User.prototype.sayHi); 
// sayHi() {
//   console.log(this.name);
// }

// 현재 프로토타입에는 메서드가 두 개입니다.
console.log(Object.getOwnPropertyNames(User.prototype)); 
// constructor, sayHi

class User {...} 문법 구조가 하는 일은 다음과 같습니다.

  • User라는 이름을 가진 함수를 만듭니다. 함수 본문은 생성자 메서드 constructor()에서 가져옵니다. 생성자 메서드가 없으면 본문이 비워진 채로 함수가 만들어집니다.

  • sayHi()같은 클래스 내에서 정의한 메서드를 User.prototype에 저장합니다.

new User를 호출해 객체를 만들고, 객체의 메서드를 호출하면 함수의 prototype 프로퍼티에서 설명한 것처럼 메서드를 프로토타입에서 가져옵니다. 이 과정이 있기 때문에 객체에서 클래스 메서드에 접근할 수 있습니다.


클래스는 단순한 편의 문법은 아닙니다.


어떤 사람들은 class 라는 키워드 없이도 클래스 역할을 하는 함수를 선언할 수 있기 때문에 클래스는 편의 문법에 불과하다고 이야기합니다. 참고로 기능은 동일하나 기존 문법을 쉽게 읽을 수 있게 만든 문법을 편의 문법(syntactic sugar, 문법 설탕)이라고 합니다.

// class User와 동일한 기능을 하는 순수 함수를 만들어보겠습니다.

// 1. 생성자 함수를 만듭니다.
function User(name) {
  this.name = name;
}

// 2. prototype에 메서드를 추가합니다.
User.prototype.sayHi = function() {
  console.log(this.name);
};

const user = new User("Tomas");
user.sayHi(); // Tomas

위 예시처럼 순수 함수로 클래스 역할을 하는 함수를 선언하는 방법과 class 키워드를 사용하는 방법의 결과는 거의 같습니다. class가 단순한 편의 문법이라고 생각하는 이유가 여기에 있습니다.

그런데 두 방법에는 중요한 차이가 몇 가지 있습니다.

  • class로 만든 함수엔 특수 내부 프로퍼티인 [[FunctionKind]]:"classConstructor"가 이름표처럼 붙습니다. 자바스크립트는 다양한 방법을 사용해 함수에 [[FunctionKind]]:"classConstructor"가 있는지를 확인합니다. 이런 검증 과정이 있기 때문에 클래스 생성자를 new와 함께 호출하지 않으면 에러가 발생합니다.

  • 클래스 메서드는 열거할 수 없습니다. 클래스의 prototype 프로퍼티에 추가된 메서드 전체의 enumerable 플래그는 false입니다.

  • for..in으로 객체를 순회할 때, 메서드는 순회 대상에서 제외하고자 하는 경우가 많으므로 이 특징은 꽤 유용합니다.

  • 클래스는 항상 엄격 모드로 실행됩니다. 클래스 생성자 안 코드 전체엔 자동으로 엄격 모드가 적용됩니다.


클래스 필드


클래스 필드라는 문법을 사용하면 어떤 종류의 프로퍼티도 클래스에 추가할 수 있습니다.

클래스 Username 프로퍼티를 추가해봅시다.

class User {
  name = "Tomas";

  sayHi() {
    console.log(`Hello, ${this.name}!`);
  }
}

new User().sayHi(); // Hello, John!

클래스를 정의할 때 <프로퍼티 이름> = <값>을 써주면 간단히 클래스 필드를 만들 수 있습니다.

클래스 필드의 중요한 특징 중 하나는 User.prototype이 아닌 개별 객체에만 클래스 필드가 설정된다는 점입니다.

class User {
  name = "Tomas";
}

const user = new User();
console.log(user.name); // Tomas
console.log(User.prototype.name); // undefined

아울러 클래스 필드엔 복잡한 표현식이나 함수 호출 결과를 사용할 수 있습니다.

class User {
  name = prompt("이름을 알려주세요.", "Tomas");
}

const user = new User();
console.log(user.name); // Tomas

클래스 필드로 바인딩된 메서드 만들기


자바스크립트의 함수는 동적인 this를 갖습니다. 따라서 객체 메서드를 여기저기 전달해 전혀 다른 컨텍스트에서 호출하게 되면 this는 원래 객체를 참조하지 않습니다.

관련 예시를 살펴봅시다. 예시를 실행하면 undefined가 출력됩니다.

class Button {
  constructor(value) {
    this.value = value;
  }

  click() {
    console.log(this.value);
  }
}

const button = new Button("hello");

setTimeout(button.click, 1000); // undefined

이렇게 this의 컨텍스트를 알 수 없게 되는 문제를 '잃어버린 this' 라고 합니다. 문제를 해결하기 위해 두 개의 방법을 사용할 수 있습니다.

  • setTimeout(() => button.click(), 1000) 같이 래퍼 함수를 전달하기

  • 생성자 안 등에서 메서드를 객체에 바인딩하기

클래스 필드는 또 다른 훌륭한 방법을 제공합니다.

class Button {
  constructor(value) {
    this.value = value;
  }
  click = () => {
    console.log(this.value);
  }
}

const button = new Button("hello");

setTimeout(button.click, 1000); // hello

클래스 필드 click = () => {...}는 각 Button 객체마다 독립적인 함수를 만들고 함수의 this를 해당 객체에 바인딩시켜줍니다. 따라서 개발자는 button.click을 아무 곳에나 전달할 수 있고, this엔 항상 의도한 값이 들어가게 됩니다.

클래스 필드의 이런 기능은 브라우저 환경에서 메서드를 이벤트 리스너로 설정해야 할 때 특히 유용합니다.
https://ko.javascript.info/bind

profile
생각 많이 하지 않기 😎

0개의 댓글