클래스는 객체 지향 프로그래밍(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' ]
클래스 필드를 사용하면 어떤 종류의 프로퍼티도 클래스에 추가할 수 있다. 클래스 User
에 name
프로퍼티를 추가해보자
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