본문으로 건너뛰기
기술스택 · stack/javascript/생성자와 new: 같은 모양 객체 찍어 내기

JavaScript · 4장 객체

생성자와 new: 같은 모양 객체 찍어 내기

회원, 상품, 주문처럼 같은 모양의 객체를 여러 개 만들 일이 많아요. 그때마다 객체를 손으로 쓰면 속성 하나를 빠뜨리기 쉬워요. 이 글에서는 객체를 찍어 내는 두 가지 방법인 팩토리 함수와 생성자 함수를 보고, 요즘 코드에서 쓰는 클래스로 이어지는 길을 짚어요.

#4.5

01. 객체를 돌려주는 함수

가장 쉬운 방법은 객체를 만들어 돌려주는 함수예요. 이런 함수를 팩토리(공장) 함수라고 불러요. 기본값을 한곳에 모을 수 있어서, 속성을 빠뜨리거나 이름을 틀릴 일이 줄어요.

function createUser(name) {
  return {
    name,
    isAdmin: false,
    joinedAt: '2026-11',
  };
}

const sky = createUser('하늘');
const river = createUser('가람');

console.log(sky);   // { name: '하늘', isAdmin: false, joinedAt: '2026-11' }
console.log(river.isAdmin); // false

팩토리 함수는 그냥 함수라서 특별한 규칙이 없어요. 실무 코드에서도 자주 보여요. 그런데 자바스크립트에는 객체를 찍어 내는 전용 문법도 있어요. 바로 new예요.

02. 생성자 함수와 new

함수를 new와 함께 부르면 그 함수는 생성자로 동작해요. 생성자 함수는 이름을 대문자로 시작하는 게 약속이에요. 함수 안에서는 객체를 직접 만들지 않고 this에 속성을 붙이기만 해요.

function User(name) {
  // this = {};          (new가 몰래 해요)
  this.name = name;
  this.isAdmin = false;
  // return this;        (new가 몰래 해요)
}

const sky = new User('하늘');

console.log(sky.name, sky.isAdmin); // 하늘 false
console.log(sky instanceof User);   // true — User로 만든 객체예요
①new User('하늘')이 하는 세 가지 일이에요. 빈 객체를 만들어 this에 연결하고, 함수 본문을 실행해 속성을 붙인 뒤, 그 객체를 돌려줘요. 돌려받은 객체가 sky에 담겨요.

정리하면 new는 ① 빈 객체를 만들어 this로 삼고 ② 본문을 실행하고 ③ this를 돌려줘요. 팩토리 함수에서 직접 쓰던 { }와 return을 new가 대신해 주는 셈이에요.

직접 생성자를 만들 일은 적어도, new는 매일 써요. new Date(), new Map(), new Error('…'), new URL('…')처럼 내장 객체를 만들 때 쓰는 것이 바로 이 문법이에요. 단, new String('a'), new Number(1), new Boolean(false)는 원시값이 아니라 객체를 만들어서 비교와 조건문에서 엉뚱하게 동작해요. 이 셋은 new 없이 String(1)처럼 형 변환에만 써요(형 변환).

03. new를 빼먹으면

생성자 함수는 생김새가 보통 함수와 같아서, new 없이 불러도 문법 오류가 나지 않아요. 그런데 그러면 this가 새 객체가 아니게 돼요. 엄격 모드에서는 this가 undefined라서 속성을 붙이다가 바로 오류가 나요.

'use strict';

function User(name) {
  this.name = name;
}

const sky = User('하늘'); // new를 빼먹었어요
console.log(sky);

엄격 모드가 아니면 더 나빠요. this가 전역 객체가 되어서 오류 없이 전역 변수 name을 덮어쓰고, sky에는 undefined가 담겨요. 버그가 엉뚱한 곳에서 터지죠. 클래스는 이런 실수를 막아 줘요. 클래스를 new 없이 부르면 Class constructor User cannot be invoked without 'new' 오류가 바로 나요.

04. 요즘은 클래스로 써요

생성자 함수는 클래스 문법이 생기기 전의 방법이에요. 지금은 같은 일을 class로 써요. 차이가 드러나는 곳은 메서드예요. 생성자 함수 안에서 this.greet = function …으로 메서드를 붙이면 객체마다 함수가 새로 하나씩 만들어져요. 클래스의 메서드는 모든 객체가 하나를 함께 써요.

function UserFn(name) {
  this.name = name;
  this.greet = function () { return `안녕, ${this.name}`; };
}

class User {
  constructor(name) { this.name = name; }
  greet() { return `안녕, ${this.name}`; }
}

const a1 = new UserFn('하늘'), a2 = new UserFn('가람');
const b1 = new User('하늘'), b2 = new User('가람');

console.log(a1.greet === a2.greet); // false — 객체마다 따로
console.log(b1.greet === b2.greet); // true — 하나를 함께
console.log(b2.greet());            // 안녕, 가람

클래스의 메서드를 여러 객체가 함께 쓰는 원리는 프로토타입(8장)이고, 클래스 문법 자체는 9장에서 자세히 다뤄요. 여기서는 "new로 객체를 찍어 내고, 생성자 안에서 this에 속성을 붙인다"는 흐름만 잡아 두면 돼요.

쿠폰 코드 세 개로 쿠폰 객체를 만들려는데 오류가 나요. 한 곳만 고쳐서 ['A10', 'B20', 'C30']과 true가 나오게 해 보세요.

'use strict';

function Coupon(code) {
  this.code = code;
  this.used = false;
}

const coupons = ['A10', 'B20', 'C30'].map(code => Coupon(code));

console.log(coupons.map(c => c.code));
console.log(coupons[0] instanceof Coupon);
해답 보기

map 안에서 Coupon(code)를 new 없이 불러서, 엄격 모드의 this가 undefined였어요. new Coupon(code)로 고치면 쿠폰 객체 세 개가 만들어져요. 처음부터 class Coupon으로 썼다면 실수했을 때 더 분명한 오류가 났을 거예요.

const coupons = ['A10', 'B20', 'C30'].map(code => new Coupon(code));
// ['A10', 'B20', 'C30']
// true

05. 실전 포인트

  • 언제 쓰나요? new는 내장 객체(Date · Map · Set · Error · URL · AbortController)와 클래스를 만들 때 써요. 생성자 함수를 직접 새로 쓸 일은 드물고, 오래된 코드나 라이브러리에서 읽게 돼요.
  • 대신 쓰는 것: 같은 모양의 객체에 메서드까지 필요하면 class(9장)를 써요. 데이터만 담는 단순한 객체라면 팩토리 함수나 객체 리터럴로 충분해요.
  • 흔한 실수: 생성자 함수를 new 없이 부르는 것, new String · new Number · new Boolean으로 원시값을 감싸는 것이에요.

정리

  • 같은 모양의 객체는 팩토리 함수나 생성자 함수로 찍어 내요.
  • new는 빈 객체를 this로 만들고, 본문을 실행하고, this를 돌려줘요.
  • new를 빼먹으면 this가 새 객체가 아니게 돼요. 클래스는 이때 바로 오류를 내 줘요.
  • 요즘 코드는 생성자 함수 대신 클래스를 쓰고, 메서드는 모든 객체가 하나를 함께 써요.
new User('하늘')을 실행하면 자바스크립트가 몰래 하는 일 세 가지는?

빈 객체를 만들어 this에 연결하고, User 본문을 실행해 속성을 붙이고, 마지막에 그 this를 돌려줘요.

엄격 모드에서 생성자 함수를 new 없이 부르면 어떻게 될까요?

this가 undefined라서 this.name = …에서 TypeError가 나요. 엄격 모드가 아니면 전역 객체에 속성이 붙어 버려서 더 찾기 어려운 버그가 돼요.

new Boolean(false)를 if 조건에 넣으면 왜 참으로 취급될까요?

new Boolean(false)는 원시값 false가 아니라 객체이고, 객체는 늘 참으로 취급되기 때문이에요. 형 변환에는 Boolean(값)처럼 new 없이 써요.

SIMPLE · 기술스택 / JavaScript — 4장 객체