[JavaScript] class - (1) 클래스와 기본 문법

경우·2022년 11월 17일
0

JavaScript

목록 보기
16/19

클래스와 기본 문법

클래스는 객체 지향 프로그래밍(OOP)에서 특정 객체를 생성하기 위해 변수와 메소드를 정의하는 일종의 틀로, 객체를 정의하기 위한 상태(멤버 변수)와 메서드(함수)로 구성된다.

☑️ 기본 문법

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

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("John");
user.sayHi();

new User("John")를 호출하면 다음과 같은 일이 일어난다.

  • 새로운 객체 생성
  • 넘겨받은 인수와 함께 constructor가 자동으로 실행. 이때 인수 "John"this.name에 할당됨

이러한 과정을 거친 후에 user.sayHi() 같은 객체 메서드를 호출할 수 있다.


☑️ 클래스란

자바스크립트에서 클래스는 함수의 한 종류이다.

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

console.log(typeof User); // function

class User { ... }가 하는 일은 다음과 같다.

  • User라는 이름을 가진 함수를 만든다. 함수 본문은 생성자 메서드 constructor에서 가져오며, 생성자 메서드가 없으면 본문이 비워진 채로 함수가 만들어진다.
  • sayHi() 같은 클래스 내에서 정의한 메서드를 User.prototype에 저장한다.

new User()를 호출해 객체를 만들고, 객체의 메서드를 호출하면 메서드를 prototype 프로퍼티를 통해 가져온다. 이 과정이 있기 때문에 객체에서 클래스 메서드에 접근할 수 있다.

class User 선언 결과를 그림으로 나타내면 다음과 같다.위의 내용을 코드로 표현하면

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); // [Function: sayHi]

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

☑️ 클래스 필드

클래스 필드를 사용하면 어떤 종류의 프로퍼티도 클래스에 추가할 수 있다. 클래스 Username 프로퍼티를 추가해보자

class User {
  name = "보라";

  sayHi() {
    console.log(`${this.name}님 안녕하세요!`);
  }
}

new User().sayHi(); // 보라님 안녕하세요!

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

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

class User {
  name = "보라";
}

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

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

class User {
  name = console.log("보라");
}

let user = new User();
user.name; // 보라

자바스크립트에서 this는 동적으로 결정된다.

따라서 객체 메서드를 여기저기 전달해 전혀 다른 컨텍스트에서 호출하게 되면 this는 메서드가 정의된 객체를 참조하지 않는다.

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

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

const button = new Button("안녕하세요.");

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

이렇게 this의 컨텍스트를 알 수 없게 되는 문제를 '잃어버린 this(losing this)'라고 한다.

두 방법을 사용해 해결할 수 있는데,

  • setTimeout(() => button.click(), 1000)와 같이 래퍼 함수를 전달하기
  • 생성자 안 등에서 메서드를 객체에 바인딩하기

가 있다.

또한 이 두 방법 말고 클래스 필드를 사용해도 문제를 해결할 수 있다.

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

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

const button = new Button("안녕하세요.");

setTimeout(button.click, 1000); // 안녕하세요.

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


exercise

함수 스타일로 작성된 코드는 다음과 같이 클래스 문법으로 작성할 수 있다.

// 함수
function Clock() {
  let timer;

  function render() {
    let date = new Date();

    let hours = date.getHours();
    if (hours < 10) hours = "0" + hours;

    let mins = date.getMinutes();
    if (mins < 10) mins = "0" + mins;

    let secs = date.getSeconds();
    if (secs < 10) secs = "0" + secs;

    console.log(`${hours}:${mins}:${secs}`);
  }

  this.start = function () {
    render();
    timer = setInterval(render, 1000);
  };
  
  this.stop = function () {
    clearInterval(timer);
  };
}

let clock = new Clock();
clock.start();
// 클래스
class Clock {
  render() {
    let date = new Date();

    let hours = date.getHours();
    if (hours < 10) hours = "0" + hours;

    let mins = date.getMinutes();
    if (mins < 10) mins = "0" + mins;

    let secs = date.getSeconds();
    if (secs < 10) secs = "0" + secs;

    console.log(`${hours}:${mins}:${secs}`);
  }

  start() {
    this.render();
    this.timer = setInterval(() => this.render(), 1000);
  }

  stop() {
    clearInterval(this.timer);
  }
}

const clock = new Clock();
clock.start();

Reference

https://ko.javascript.info/

0개의 댓글