본문으로 건너뛰기

클래스와 new

자바스크립트 목차클로저와 클래스레슨 51 / 76

목차

객체: 이름 붙은 칸 묶음에서는 { owner: '민지', stamps: 0 }⁠처럼 중괄호로 객체를 하나씩 직접 적었습니다. 손님이 열 명이면 같은 모양의 객체를 열 번 반복해 적어야 하고, 속성 이름을 오타 내기도 쉽습니다. **class(클래스)**로 객체의 틀을 한 번 정의해 두면, new 연산자로 부를 때마다 같은 속성 구성을 가진 새 객체(인스턴스)를 찍어 낼 수 있습니다.

8단계 중 1단계. 7번 줄의 `new Counter(0)`이 빈 `Counter` 객체 ①을 새로 만들고 `constructor` 호출 칸을 엽니다. `this`는 새 객체 ①을, `start`는 `0`을 가리킵니다.

1class와 new — 같은 모양의 객체를 찍어 내는 틀

클래스는 class 뒤에 이름(보통 첫 글자를 대문자로 적습니다)과 중괄호를 써서 정의합니다. 중괄호 안의 **constructor(생성자)**는 new로 새 객체를 만들 때 자동으로 불리는 초기화 함수입니다.

class Counter {
  constructor(start) {
    this.count = start;
  }
}

const a = new Counter(0);
const b = new Counter(10);
console.log(a.count, b.count);
실행 결과
0 10

new Counter(0)을 실행하면 세 가지 일이 차례로 일어납니다(함수 만들고 부르기의 규칙 5 확장).

  1. 빈 Counter 객체 ①이 새로 만들어집니다.
  2. constructor 호출 칸이 열리고, 그 칸의 this 이름표가 방금 만든 빈 객체 ①을 자동으로 가리킵니다. 인자 0은 매개변수 start에 붙습니다.
  3. this.count = start가 실행되어 this가 가리키는 객체 ① 안에 count: 0 속성을 채웁니다. constructor가 끝나면 return을 적지 않아도 객체 ①이 자동으로 돌아와 a에 붙습니다.

이어서 8번 줄에서 new Counter(10)을 부르면 두 번째 Counter 객체 ②가 따로 만들어지고, 그때의 this는 객체 ②를 가리킵니다. 이렇게 클래스로 만든 객체를 그 클래스의 **인스턴스(instance)**라고 부릅니다.

2고정된 첫 값은 클래스 필드로도 적는다

플로우 카페의 스탬프 카드를 클래스로 만들어 보겠습니다. 손님 이름(owner)은 카드를 만들 때마다 다르지만, 처음 도장 개수(stamps)는 늘 0에서 시작합니다.

class StampCard {
  constructor(owner) {
    this.owner = owner;
    this.stamps = 0;
  }
}

const c = new StampCard('민지');
console.log(c.owner, c.stamps);
실행 결과
민지 0

늘 같은 값(0)으로 시작하는 속성은 constructor 밖 맨 위에 stamps = 0;처럼 바로 적어도 됩니다. 이 표기를 **클래스 필드(class field)**라고 합니다.

class StampCard {
  stamps = 0;
  constructor(owner) {
    this.owner = owner;
  }
}

const c = new StampCard('민지');
console.log(c.owner, c.stamps);
실행 결과
민지 0

Node에서 인스턴스 자체를 console.log로 찍으면 앞에 클래스 이름과 함께 속성들이 중괄호로 묶여 출력됩니다.

class Card {
  constructor(owner) {
    this.owner = owner;
  }
}
const c = new Card('민지');
console.log(c);
실행 결과
Card { owner: '민지' }

3new를 두 번 부른 것과 이름만 하나 더 붙인 것

객체를 넘기고 복사하기에서 배운 규칙 3은 클래스로 만든 인스턴스에도 그대로 적용됩니다. 다음 코드의 출력을 먼저 맞혀 보세요.

class Counter {
  constructor(start) {
    this.count = start;
  }
}

이어서 a, b, same을 만들고 a.count를 1로 바꿉니다.

const a = new Counter(0);
const b = new Counter(0);
const same = a;
a.count = 1;
console.log(b.count);
console.log(same.count);

b.count와 same.count는 각각 얼마가 찍힐까요?

실행 결과 보기
실행 결과
0
1
  • const b = new Counter(0)⁠은 new를 다시 불렀으므로 a(객체 ①)와 별개인 새 객체 ②를 만듭니다. a.count를 바꿔도 b.count는 0 그대로입니다.
  • const same = a는 new를 부르지 않고 a가 가리키던 같은 객체 ①에 same이라는 이름표만 하나 더 붙인 것입니다. 따라서 same.count로 읽으면 바뀐 1이 나옵니다.

또한 클래스를 부를 때 new를 빠뜨리고 일반 함수처럼 Counter(0)만 부르면 바로 TypeError가 납니다.

class Counter {
  constructor(start) {
    this.count = start;
  }
}
const a = Counter(0);
실행 결과
main.js:6const a = Counter(0);          ^ TypeError: Class constructor Counter cannot be invoked without 'new'    at main.js:6:11

예제

플로우 카페 손님 두 명('민지', '준호')의 스탬프 카드를 각각 만들고, 민지의 카드에만 도장 1개를 찍어 두 카드가 서로 섞이지 않는지 확인해 보겠습니다.

class StampCard {
  constructor(owner) {
    this.owner = owner;
    this.stamps = 0;
  }
}

이어서 카드 두 장을 만들고 민지의 도장 수를 올립니다.

const c1 = new StampCard('민지');
const c2 = new StampCard('준호');
c1.stamps += 1;
console.log(c1.stamps);
console.log(c2.stamps);
실행 결과
1
0

new StampCard(...)를 두 번 불렀으므로 c1과 c2는 서로 다른 두 객체를 가리킵니다. c1.stamps를 1로 올려도 c2.stamps는 0 그대로입니다.

자주 틀리는 곳

1. new를 빠뜨리고 클래스를 일반 함수처럼 부름

클래스 생성자는 new 없이 부를 수 없습니다. StampCard('민지')처럼 new를 빼고 쓰면 TypeError 오류가 납니다.

2. constructor 안에서 this.를 빠뜨림

constructor 안에서 새 객체에 속성을 넣으려면 꼭 this.stamps = 0;처럼 this.를 붙여야 합니다. let이나 this. 없이 stamps = 0;만 적으면 선언하지 않은 변수에 대입하려 한 것으로 보아 ReferenceError가 납니다.

class StampCard {
  constructor(owner) {
    this.owner = owner;
    stamps = 0;
  }
}
const c = new StampCard('민지');
실행 결과
main.js:4    stamps = 0;           ^ ReferenceError: stamps is not defined    at new StampCard (main.js:4:12)    at main.js:7:11

3. 클래스 자체에서 인스턴스 속성을 읽으려 함

StampCard는 객체를 찍어 내는 틀일 뿐 아직 찍어 낸 카드가 아닙니다. new StampCard('민지')로 인스턴스를 만든 뒤 그 인스턴스(c1.stamps)에서 속성을 읽어야 합니다.

확인 문제

문제 1

다음 코드를 실행했을 때 출력되는 결과를 쓰세요.

class Box {
  constructor(qty) {
    this.qty = qty;
  }
}

이어서 상자 두 개를 만들고 값을 바꿉니다.

const x = new Box(3);
const y = new Box(3);
x.qty += 2;
console.log(x.qty, y.qty);
힌트

new Box(3)을 두 번 불렀으므로 x와 y는 서로 다른 두 Box 객체를 가리킵니다.

정답
실행 결과
5 3

x가 가리키는 첫 번째 상자의 qty만 3에서 5로 바뀌고, y가 가리키는 두 번째 상자의 qty는 3 그대로입니다.

문제 2

메뉴 이름 name과 가격 price를 받아 저장하는 Menu 클래스를 완성하고, '커피', 4000으로 새 인스턴스를 만들도록 빈칸 ____ 두 곳을 채우세요.

class Menu {
  constructor(name, price) {
    ____.name = name;
    this.price = price;
  }
}

const m =
  ____ Menu('커피', 4000);
console.log(m.name, m.price);
힌트

constructor 안에서 새로 만들어진 객체를 가리키는 이름표는 this이고, 클래스로 새 객체를 만들 때는 앞에 new를 붙입니다.

정답
  • 첫째 빈칸: this
  • 둘째 빈칸: new
class Menu {
  constructor(name, price) {
    this.name = name;
    this.price = price;
  }
}

const m =
  new Menu('커피', 4000);
console.log(m.name, m.price);
실행 결과
커피 4000

문제 3

주인 이름 owner와 초기 잔액 money를 받아 속성에 저장하는 Wallet 클래스를 직접 쓰세요.

기대 출력과 시험 값은 다음과 같습니다.

민지
5000
// 여기에 Wallet 클래스를 쓰세요

const w =
  new Wallet('민지', 5000);
console.log(w.owner);
console.log(w.money);
힌트

class Wallet 안에 constructor(owner, money)⁠를 만들고 this.owner = owner;와 this.money = money;를 적습니다.

정답
class Wallet {
  constructor(owner, money) {
    this.owner = owner;
    this.money = money;
  }
}

const w =
  new Wallet('민지', 5000);
console.log(w.owner);
console.log(w.money);
실행 결과
민지
5000

컴퓨터에서 해 보기(선택) — 1절 코드에서 const a = new Counter(0);⁠의 new를 지우고 돌려 보세요. 어떤 오류 메시지가 몇 번 줄에서 나오는지 확인한 뒤 다시 new를 붙여 고쳐 보세요.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

자바스크립트 목차로