Javascript Class Basics

Garam·2023년 10월 30일
0

The Odin Project

목록 보기
10/14
post-thumbnail

Chapter link: https://www.theodinproject.com/lessons/node-path-javascript-classes
Translated from: https://javascript.info/class


The "class" syntax

class MyClass {
  // class methods
  constructor() { ... }
  method1() { ... }
  method2() { ... }
  method3() { ... }
  ...
}

객체 생성은 new MyClass()를 사용한다.
constructor() 메소드는 new에 의해 자동으로 호출되고 객체를 초기화 한다.


class User {

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

  sayHi() {
    alert(this.name);
  }

}

// Usage:
let user = new User("John");
user.sayHi();

new User("John")이 호출될 때:
1. 새 객체가 생성된다.
2. constructor가 발동하고 인수를 this.name에 할당한다.
... 이후 우리는 user.sayHi()와 같은 객체의 메소드를 발동할 수 있게 된다.



What is a class?

자바스크립트에서 class는 일종의 함수처럼 기능한다.

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

// proof: User is a function
alert(typeof User); // function

class User {...}는 실제로 어떻게 기능하는가?

  1. 클래스 선언의 결과가 되는User라는 함수를 만든다. 함수의 코드는 constructor 메소드에서 가져온다. (만약 해당 메소드를 작성하지 않았다면 비어있다고 가정한다)
  2. sayHi와 같은 클래스 메소드를 User.prototype에 저장한다.

new User 객체가 만들어지고 나면, 우리가 호출하는 메소드는 prototype에 저장된 것을 꺼내오는 것이다. 따라서 객체는 클래스 메소드에 접근할 수 있다.

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

// class is a function
alert(typeof User); // function

// ...or, more precisely, the constructor method
alert(User === User.prototype.constructor); // true

// The methods are in User.prototype, e.g:
alert(User.prototype.sayHi); // the code of the sayHi method

// there are exactly two methods in the prototype
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi


Not just a syntactic sugar

// rewriting class User in pure functions

// 1. Create constructor function
function User(name) {
  this.name = name;
}
// a function prototype has "constructor" property by default,
// so we don't need to create it

// 2. Add the method to prototype
User.prototype.sayHi = function() {
  alert(this.name);
};

// Usage:
let user = new User("John");
user.sayHi();

자바스크립트에서는 class를 사용하지 않고도 같은 결과를 불러오는 코드를 얼마든지 작성할 수 있다. 이런 특성 때문에 때때로 사람들은 자바스크립트의 클래스는 새로운 기능이 없는, 그저 가독성을 위해 만들어진 기능이라고 말한다.


그럼에도 불구하고 class에는 중요한 특성이 내포되어 있다.

  1. class에 의해 만들어진 함수는 [[IsClassConstructor]]: true라는 내부적 속성을 가진다.
    자바스크립트는 다양한 상황에서 해당 속성을 체크한다. 예를 들어 보통 함수와는 달리 클래스는 항상 new를 통해서만 호출되어야 한다.
class User {
  constructor() {}
}

alert(typeof User); // function
User(); // Error: Class constructor User cannot be invoked without 'new'
alert(User); // class User { ... }
  1. 클래스 메소드는 열거가 불가능하다. 클래스는 "prototype"의 모든 메소드의 enumerable 플래그에 false를 부여한다.
    따라서 우리가 객체에 for...in을 사용할 때 클래스 메소드를 불러오지 않을 수 있다.

  2. 클래스는 항상 strict 모드에서 쓰여진다.



Class Expression

함수와 마찬가지로 클래스도 다른 표현식의 안에서 정의되거나, 여기저기로 전달되거나, 리턴되거나 할당될 수 있다.

let User = class {
  sayHi() {
    alert("Hello");
  }
};

기명 함수 표현식(Named FUnction Expressions)처럼 클래스 표현식도 이름을 가질 수 있다.
클래스를 기명 함수 표현식을 통해 작성하면, 클래스의 이름은 클래스 내부에서만 보인다.

// "Named Class Expression"
// (no such term in the spec, but that's similar to Named Function Expression)
let User = class MyClass {
  sayHi() {
    alert(MyClass); // MyClass name is visible only inside the class
  }
};

new User().sayHi(); // works, shows MyClass definition

alert(MyClass); // error, MyClass name isn't visible outside of the class

아래와 같이 좀 더 역동적으로 수요에 그때그때 대응하는 클래스를 만들수도 있다.

function makeClass(phrase) {
  // declare a class and return it
  return class {
    sayHi() {
      alert(phrase);
    }
  };
}

// Create a new class
let User = makeClass("Hello");

new User().sayHi(); // Hello


Getters/setters

다른 literal object들과 같이, 클래스도 getters/setters나 computed properties를 쓸 수 있다.

class User {

  constructor(name) {
    // invokes the setter
    this.name = name;
  }

  get name() {
    return this._name;
  }

  set name(value) {
    if (value.length < 4) {
      alert("Name is too short.");
      return;
    }
    this._name = value;
  }

}

let user = new User("John");
alert(user.name); // John

user = new User(""); // Name is too short.

이러한 클래스 선언은 getters와 setters를 User.prototype에 저장함으로써 기능한다.



Computed names [...]

class User {

  ['say' + 'Hi']() {
    alert("Hello");
  }

}

new User().sayHi();


Class fields

❗️ Class fields는 최근에 추가된 기능으로 구버전의 브라우저에서 작동하지 않을 수 있다.

"Class fields"는 메소드 뿐 아니라 다른 속성들을 클래스에 추가해줄 수 있는 구문이다.

예를 들어 다음과 같이 class Username이라는 속성을 부여할 수 있다.

class User {
  name = "John";

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

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

"="를 써서 선언하기만 하면 된다.

Class fields의 중요한 점은 바로 해당 속성이 User.prototype이 아닌 각각의 객체에만 세팅이 된다는 것이다.

class User {
  name = "John";
}

let user = new User();
alert(user.name); // John
alert(User.prototype.name); // undefined

다음과 같이 좀더 복잡한 표현식이나 함수 호출을 할당할 수도 있다.

class User {
  name = prompt("Name, please?", "John");
}

let user = new User();
alert(user.name); // John


Making bound methods with class fields

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

  click() {
    alert(this.value);
  }
}

let button = new Button("hello");

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

위 코드는 도중에 this를 잃어버리는 문제점을 내포하고 있다.

이를 바로잡기 위해 Function binding 에서 언급되었던 두가지 해결방안을 도입할 수 있다.

  1. wrapper function을 패스한다:
    setTimeout(() => button.click(), 1000)
  2. 메소드를 객체에 bind한다. (예: constructor 안에)

Class fields는 이에 더해 좀 더 우아한 방식을 제공한다.

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

let button = new Button("hello");

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

Class field click = () => {...}는 각각의 객체를 기반으로 만들어진다. 따라서 각각의 Button 객체는 해당 객체와 연결된this를 가진 서로 다른 함수를 갖고 있게 되는 것이다. 이 경우 this의 값은 언제나 뜻대로 움직이게 된다.

이 기능은 특히 브라우저 환경에서 이벤트 리스너를 사용할 때 유용하다.



Summary

기본적인 클래스 구문은 다음과 같다.

class MyClass {
  prop = value; // property

  constructor(...) { // constructor
    // ...
  }

  method(...) {} // method

  get something(...) {} // getter method
  set something(...) {} // setter method

  [Symbol.iterator]() {} // method with computed name (symbol here)
  // ...
}

종합적으로 MyClass는 메소드, getters, setters가 MyClass.prototype에 씌여진, constructor로써 제공되는 함수라고 정의할 수 있다.


0개의 댓글