JavaScript new 연산자

2025. 9. 9. 15:43·FrontEnd/JavaScript

new는 그냥 "객체 만드는 키워드"가 아니다.
this를 어떻게 묶고, 어떤 과정을 거쳐 인스턴스를 만드는지 아는 게 핵심이다. 🧑‍💻

예전에 자바스크립트 공부할 때,

  • "생성자 함수에는 무조건 new 붙여라"
  • "클래스는 new 없으면 에러 난다"

이런 말만 듣고 넘겨버리기 쉬운데,
정작 new가 내부에서 무슨 일을 하는지 모르면
버그가 생겨도 감으로만 디버깅하게 된다.

이번 글에서는 new가 실제로 하는 일을
4단계 과정으로 풀어서 정리해본다.


결론 먼저

한 줄 요약하면

  • new = 새 객체(인스턴스)를 만들고, 그 안에서 this를 알맞게 바인딩해 주는 연산자
  • 내부적으로는 대략 이런 일이 일어난다.
    1. 빈 객체 하나 만든다
    2. 그 객체에 프로토타입을 연결하고, this로 묶는다
    3. 생성자 함수(또는 클래스의 constructor)를 실행해서 초기화한다
    4. 함수가 따로 객체를 반환하지 않았다면, 그 새 객체를 반환한다
  • new를 빼먹고 호출하면
    • 구버전/비엄격 모드: this가 전역 객체(window)를 가리켜서 전역 오염 위험
    • 엄격 모드: this가 undefined라서 TypeError가 터질 수 있음

new 연산자, 실제로는 이렇게 동작한다

조금 더 구조적으로 보면, new Foo(...)는 대략 다음과 같은 일을 한다고 볼 수 있다.

  1. 새 객체를 하나 만든다
    • 일종의 빈 껍데기 {}를 하나 만든다고 생각하면 편하다.
  2. 프로토타입을 연결한다
    • 새 객체의 [[Prototype]]을 Foo.prototype으로 설정한다.
  3. 그 객체를 this로 삼아서 생성자 함수를 실행한다
    • Foo.call(새객체, ...인자) 이런 느낌.
  4. 반환값 결정
    • 생성자 함수가 별도의 객체를 return하면 그걸 그대로 돌려준다.
    • 아니면 2~3번에서 만들고 초기화한 그 새 객체를 반환한다.

이 과정을 머릿속에만 넣고 넘기기 아쉬우니까,
실제 코드와 함께 보는 게 더 이해가 잘 된다.


생성자 함수 예시

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const person = new Person("John", 30);
console.log(person);
// Person { name: "John", age: 30 }

여기서 new Person("John", 30)을 위 4단계에 맞춰서 풀어 쓰면

  1. 빈 객체 하나 만든다 → const obj = {} 같은 느낌
  2. obj.__proto__를 Person.prototype으로 연결
  3. Person 함수를 this = obj로 실행 → this.name = name 등으로 속성 세팅
  4. Person이 객체를 따로 반환하지 않았으니, obj를 반환 → 그게 person

그래서 person은

  • name, age 프로퍼티를 가지는 객체이고
  • 프로토타입 체인을 통해 Person.prototype에 정의된 메서드를 사용할 수 있다.

클래스 예시 (class 문법)

class 문법을 쓰면 문법이 좀 더 깔끔해질 뿐,
new가 하는 큰 흐름은 비슷하다.

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

const person = new Person("Jane", 25);
console.log(person);
// Person { name: "Jane", age: 25 }

여기서도 마찬가지로

  1. 새 객체 생성
  2. 그 객체의 프로토타입을 Person.prototype으로 연결
  3. constructor를 this에 묶어서 실행
  4. 초기화된 객체 반환

단, class는 무조건 new와 함께 써야 한다는 점이 다르다.

Person("Jane", 25); // TypeError: Class constructor Person cannot be invoked without 'new'

생성자 함수는 실수로 new를 빼먹어도 조용히 망가질 수 있지만,
클래스는 아예 에러를 내주기 때문에 디버깅이 조금 더 수월하다.


new 없이 호출하면 왜 위험할까?

생성자 함수 스타일에서 new를 빼먹으면 문제가 생긴다.

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

Person("John"); // new 없이 호출

비엄격 모드(옛 코드, 브라우저 기본 스크립트 등)

  • this가 전역 객체(window)를 가리킨다.
  • 그래서 window.name = "John"처럼 전역이 오염될 수 있다.
console.log(window.name); // "John" 같은 식으로 찍힐 수 있음

엄격 모드("use strict")

  • this가 undefined가 되며,
  • this.name = ... 같은 코드에서 TypeError가 터질 수 있다.

즉, 둘 다 좋은 상황이 아니다.

그래서 관례적으로

  • 생성자 역할을 하는 함수는 이름을 대문자로 시작하고 (Person, User 등)
  • 호출할 때는 항상 new Person(...) 형태로 쓰는 습관을 들인다.

new 직접 흉내 내보기 (myNew)

설명만 들으면 감이 안 오니까,
new 연산자의 동작을 직접 함수로 흉내 내보면 이해가 더 잘 된다.

function myNew(constructor, ...args) {
  // 1. constructor.prototype을 프로토타입으로 가지는 새 객체 생성
  const obj = Object.create(constructor.prototype);

  // 2. this를 obj로 바인딩해서 생성자 호출
  const result = constructor.apply(obj, args);

  // 3. 생성자가 명시적으로 객체를 반환하면 그걸 사용
  //    아니면 우리가 만든 obj를 반환
  return result instanceof Object ? result : obj;
}

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

const person = myNew(Person, "John");
console.log(person);
// Person { name: "John" }

이렇게 직접 구현해보면

  • 왜 constructor.prototype이 중요한지
  • 왜 this 바인딩이 핵심인지
  • 왜 "생성자가 객체를 반환하면 그걸 우선한다"고 하는지

감각적으로 이해하기가 훨씬 쉽다.


체크리스트

실제 코드에서 new를 쓸 때, 머릿속에서 대충 이렇게 점검하면 좋다

  • 이 함수/클래스는 생성자 역할을 하는가?
    • → 그렇다면 이름을 대문자로 시작하는 게 관례 (Person, User, Order 등)
  • 호출할 때 new를 빼먹지 않았는가?
    • 생성자 함수 스타일이라면, new를 빼먹어도 조용히 전역을 건드릴 수 있다.
  • 클래스(class)는 항상 new와 함께 쓰고 있는가?
  • 생성자에서 굳이 return { ... } 같은 객체 반환을 하고 있는가?
    • 정말 필요할 때만 쓰고, 대부분은 this에 값만 채우고 return은 생략한다.

오늘 내용 한 줄씩 다시 정리

  • new는 단순히 "객체 하나 만들어 줘"가 아니라,
    • 새 객체를 만들고
    • 프로토타입을 연결하고
    • this를 바인딩해서 생성자/클래스를 실행하고
    • 최종 객체를 반환해주는 전체 프로세스다.
  • 생성자 함수에 new를 빼먹으면
    • 비엄격 모드: 전역 객체 오염
    • 엄격 모드: this 관련 에러 발생
  • class 문법에서는
    • new를 안 쓰면 바로 에러를 내주기 때문에,
    • 생성자 함수보다 실수를 잡아주기 좀 더 쉽다.

정리하자면

생성자 역할을 하는 함수/클래스 이름은 대문자로 시작하고,

  • 호출할 땐 항상 new를 붙이는 습관을 들이자.
    그렇게만 해도, this와 전역 오염으로 인한 많은 버그를 미리 막을 수 있다.
저작자표시 변경금지 (새창열림)

'FrontEnd > JavaScript' 카테고리의 다른 글

JavaScript 컬렉션 - Object  (0) 2025.09.09
JavaScript 반복문 한 번에 정리하기  (0) 2025.09.09
JavaScript 합성(Composition) - 상속보다 유연하게 객체 설계하기  (0) 2025.09.09
ES6 클래스 vs 생성자 함수 - 요즘 자바스크립트에서 뭘 써야 할까?  (0) 2025.09.09
DOM으로 HTML 다루기 - innerHTML, insertAdjacentHTML, createContextualFragment  (0) 2025.09.09
'FrontEnd/JavaScript' 카테고리의 다른 글
  • JavaScript 컬렉션 - Object
  • JavaScript 반복문 한 번에 정리하기
  • JavaScript 합성(Composition) - 상속보다 유연하게 객체 설계하기
  • ES6 클래스 vs 생성자 함수 - 요즘 자바스크립트에서 뭘 써야 할까?
프론트엔드 개발자 jbeat
프론트엔드 개발자 jbeat
프론트엔드 개발자 블로그인데 일상도 쪼그으믐
  • 프론트엔드 개발자 jbeat
    jbeat 님의 블로그
    프론트엔드 개발자 jbeat
  • 전체
    오늘
    어제
    • 분류 전체보기 (44)
      • FrontEnd (43)
        • TypeScript (6)
        • JavaScript (18)
        • Next.js (3)
        • React (1)
        • Testing (2)
        • Third Party (1)
        • web (10)
        • Tooling (1)
        • coding test (0)
        • A.I (1)
      • 일상 (1)
        • wedding (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 인기 글

  • 태그

    Android
    Next.js
    타입스크립트
    playwright
    고차함수
    omit
    주니어
    TypeScript
    컬렉션
    javascript
    yjs
    CrossOrigin
    배열
    이터러블
    Utility
    WebSocket
    preconnect
    pick
    CRDT
    코테
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
프론트엔드 개발자 jbeat
JavaScript new 연산자
상단으로

티스토리툴바