객체와 프로토타입 (Object and Prototype)

한눈에 보기

JavaScript의 모든 객체는 프로토타입을 내부 링크로 참조하며, 프로토타입 체인을 따라 프로퍼티를 탐색해 상속을 구현한다. prototype과 __proto__의 구분, class 문법, 프로토타입 오염까지 다룬다.

최종 업데이트 2026.09.17

JavaScript의 객체(Object)는 키와 값의 쌍인 프로퍼티(Property)의 집합이며, 모든 객체는 프로토타입(Prototype)이라 불리는 또 다른 객체를 내부 링크로 참조한다. JavaScript는 클래스 기반 상속 대신 이 링크를 따라 프로퍼티를 탐색하는 프로토타입 체인(Prototype Chain)으로 상속을 구현하는 프로토타입 기반 언어다. class 문법 역시 내부적으로는 이 메커니즘 위에서 동작한다.

객체를 만드는 방법

객체는 객체 리터럴, 생성자 함수, Object.create, class 문법으로 만들 수 있다. 어떤 방법을 쓰든 만들어진 객체는 자신의 프로토타입에 대한 내부 슬롯 [[Prototype]]을 가진다. 리터럴로 만든 객체의 프로토타입은 Object.prototype이고, 생성자 함수로 만든 인스턴스의 프로토타입은 그 함수의 prototype 프로퍼티가 가리키는 객체다.

const literal = { name: "kim" };            // 프로토타입은 Object.prototype

function User(name) { this.name = name; }
const u = new User("lee");                  // 프로토타입은 User.prototype

const bare = Object.create(null);           // 프로토타입이 없는 순수 객체

prototype과 __proto__의 구분

면접에서 가장 많이 혼동하는 지점이 prototype 프로퍼티와 [[Prototype]] 링크의 구분이다. prototype은 생성자로 쓰일 함수만 갖는 프로퍼티로, 그 함수로 만들 인스턴스들의 프로토타입이 될 객체를 가리킨다. 반면 [[Prototype]]은 모든 객체가 갖는 내부 링크이며, 과거에는 __proto__ 접근자로 읽었지만 현재 표준은 Object.getPrototypeOf와 Object.setPrototypeOf 사용을 권장한다.

구분prototype[[Prototype]] (__proto__)
누가 갖는가생성자 함수와 클래스모든 객체
무엇을 가리키는가미래 인스턴스들의 프로토타입이 될 객체자신의 부모에 해당하는 프로토타입 객체
접근 방법Func.prototypeObject.getPrototypeOf(obj)

따라서 new User("lee")로 만든 인스턴스 u에 대해 Object.getPrototypeOf(u) === User.prototype이 성립한다.

프로토타입 체인의 탐색 규칙

객체의 프로퍼티를 읽을 때 JavaScript 엔진은 먼저 객체 자신의 프로퍼티(Own Property)를 확인하고, 없으면 [[Prototype]] 링크를 따라 올라가며 찾는다. 체인의 끝은 Object.prototype이고 그 프로토타입은 null이다. 끝까지 찾지 못하면 undefined가 반환된다. 배열에서 map을 호출할 수 있는 이유도 배열 인스턴스에는 map이 없지만 Array.prototype에 정의되어 있고 체인을 통해 공유되기 때문이다.

const animal = {
  eat() { return "먹는다"; }
};
const dog = Object.create(animal);
dog.bark = function () { return "짖는다"; };

dog.bark(); // 자신의 프로퍼티에서 발견
dog.eat();  // animal에서 발견, 체인을 따라 탐색
dog.fly;    // undefined, 체인 끝까지 없음

중요한 점은 이것이 복사가 아니라 위임(Delegation)이라는 사실이다. 프로토타입의 메서드는 하나만 존재하고 모든 인스턴스가 공유하므로 메모리 효율이 좋고, 프로토타입을 수정하면 기존 인스턴스에도 즉시 반영된다. 반면 쓰기 연산은 체인을 따라 올라가지 않고 항상 객체 자신에게 프로퍼티를 만들기 때문에, 같은 이름의 프로퍼티가 프로토타입의 것을 가리는 섀도잉(Shadowing)이 일어난다.

생성자 함수에서 class 문법으로

ES2015의 class는 생성자 함수와 프로토타입 조작을 선언적으로 감싼 문법적 설탕(Syntactic Sugar)이다. 클래스 본문에 정의한 메서드는 자동으로 prototype 객체에 놓이고, extends는 인스턴스 체인과 정적 멤버 체인을 함께 연결한다. 다만 class는 new 없이 호출하면 오류가 나고, 클래스 선언은 호이스팅되어도 일시적 사각지대의 적용을 받는 등 일반 함수와 동작이 완전히 같지는 않다.

class Animal {
  constructor(name) { this.name = name; }
  eat() { return `${this.name}가 먹는다`; }
}
class Dog extends Animal {
  bark() { return "멍멍"; }
}
const d = new Dog("초코");
d.eat();  // Animal.prototype에서 위임받아 실행

주의할 점

for...in 반복문은 프로토타입 체인에서 상속된 열거 가능 프로퍼티까지 순회하므로, 객체 자신의 키만 다루려면 Object.hasOwn(obj, key)나 Object.keys를 사용해야 한다. 또한 Object.prototype처럼 널리 공유되는 내장 프로토타입을 수정하는 일은 모든 객체의 동작을 바꾸는 것이므로 피해야 한다. 외부 입력으로 객체 키를 만들 때 __proto__ 같은 키를 그대로 받아들이면 프로토타입 오염(Prototype Pollution) 공격으로 이어질 수 있으므로, 맵(Map)을 쓰거나 Object.create(null)로 만든 객체를 사전 용도로 쓰는 것이 안전하다.

면접에서 이렇게 물어본다

  • prototype 프로퍼티와 __proto__의 차이를 정확히 구분해서 설명해 보라.
  • 프로토타입 체인의 탐색 과정을 배열의 map 메서드 호출을 예로 들어 설명해 보라.
  • class 문법이 문법적 설탕이라고 불리는 이유와 생성자 함수와의 차이점은 무엇인가.
  • 프로토타입에 메서드를 정의하는 방식이 메모리 측면에서 유리한 이유를 설명해 보라.
  • 프로토타입 오염 공격이 무엇이고 어떻게 방어할 수 있는가.
JavaScript / Python 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기