자바스크립트 객체의 프로토타입을 다루는 방법

자바스크립트는 프로토타입(prototype)이라는 독특한 프로그래밍 패러다임을 가진 프로그래밍 언어입니다. 자바스크립트에서 클래스 기반 객체 지향 프로그래밍이 가능한 이유도 결국은 이 프로토타입 덕분이죠.

이번 시간에서는 자바스크립트에서 객체의 프로토타입을 어떻게 다뤄야하는지 알아보겠습니다

__proto__ 속성

자바스크립트에서 모든 객체는 자신의 프로토타입(prototype)을 __proto__이라는 비밀 속성에 저장하고 있습니다.

한 번 브라우저 콘솔에서 빈 객체를 생성한 후에 __proto__ 속성을 확인해보세요.

const obj = {};
obj.__proto__;

그러면 여러 가지 속성으로 이루어진 객체가 확인이 될 거에요. 이 객체가 바로 모든 객체의 원형, 즉 프로토타입입니다.

{
  constructor: function Object() { [native code] }
  hasOwnProperty: function hasOwnProperty() { [native code] }
  isPrototypeOf: function isPrototypeOf() { [native code] }
  propertyIsEnumerable: function propertyIsEnumerable() { [native code] }
  toLocaleString: function toLocaleString() { [native code] }
  toString: function toString() { [native code] }
  valueOf: function valueOf() { [native code] }
  /* 그 밖에 다른 속성들 ... */
}

여기서 한 가지 기억하실 점은 __proto__는 비밀 속성이며 MDN 공식 문서를 보시면 현재 폐기된(deprecated) 속성입니다. 따라서 애플리케이션 코드를 작성하실 때 __proto__ 속성을 직접적으로 참조하는 것은 권장되지 않습니다.

웹 표준에 맞게 객체의 프로토타입에 접근하는 방법은 아래에서 자세히 다루도록 하겠습니다.

프로토타입 설정

객체의 프로토타입은 일반적으로 객체를 생성할 때 생성자 함수에 의해 설정됩니다. 생성자 함수의 prototype 속성이 가리키고 있는 객체를, 그 생성자 함수로 만든 객체의 __proto__ 속성도 가리키게 되지요.

const date = new Date();
date; // {toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, toUTCString: ƒ, …}
date.__proto__ === Date.prototype; // true

이 것은 사용자가 정의한 클래스도 마찬가지입니다.

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

  getInfo() {
    return `Name: ${this.name}`;
  }
}

const person = new Person();
person; // {getInfo: ƒ}
person.__proto__ === Person.prototype; // true

객체를 생성하면서 프로토타입을 설정하는 또 다른 방법은 Object.create() 정적(static) 메서드를 사용하는 것입니다. Object.create() 메서드로 객체를 생성할 때 인자로 프로토타입을 넘기면 반환된 객체의 프로토타입으로 설정됩니다.

const date = Object.create(Date.prototype);
date.__proto__ === Date.prototype; // true

자주 쓰이는 방법은 아니지만, 중괄호({}) 기호를 통해서 객체 리터럴(literal)을 만들 때는 __proto__ 속성에 바로 해당 객체의 프로토타입을 지정해줄 수도 있습니다.

const person = { name: "Dale", __proto__: Person.prototype };
person.getInfo(); // 'Name: Dale'
person.__proto__ === Person.prototype; // true

이렇게 3가지 방법을 통해서 프로토타입을 설정해줄 수 있습니다.

프로토타입 확인

대부분의 브라우저에서 객체의 프로토타입은 __proto__ 속성에 저장되어 있지만, 위에서 말씀드린 것과 같이 __proto__ 속성에 바로 접근하는 것은 웹 표준이 아니라서 피해야 합니다.

const date = new Date();
const proto = date.__proto__; // ❌
proto === Date.prototype; // true

대신 Object 클래스의 getPrototypeOf() 정적 메서드에 객체를 인자로 넘기면 해당 객체의 프로토타입이 무엇인지 알아낼 수 있습니다.

const date = new Date();
const proto = Object.getPrototypeOf(date); // ✅
proto === Date.prototype; // true

다양한 방법으로 생성한 날짜 객체를 상대로 Object.getPrototypeOf() 메서드를 테스트해보겠습니다.

Object.getPrototypeOf(new Date()) === Date.prototype; // true
Object.getPrototypeOf(Object.create(Date.prototype)) === Date.prototype; // true
Object.getPrototypeOf({ __proto__: Date.prototype }) === Date.prototype; // true

뿐만 아니라, Object.getPrototypeOf() 메서드를 연쇄적으로 호출하여 객체의 프로토타입 체인(prototype chain)을 탐색할 수도 있습니다.

Object.getPrototypeOf(Object.getPrototypeOf(new Date())) === Object.prototype;

날짜 객체의 프로토타입의 프로토타입Object 클래스의 prototype 속성과 동일한 것을 확인할 수 있습니다.

프로토타입 변경

객체의 프로토타입을 변경할 때도 객체 외부에서 폐기된 __proto__ 비밀 속성에 접근하는 것은 웹 표준에 위배됩니다.

const person = {};
person.__proto__ = Person.prototype; // ❌
person.name = "Dale";
person.getInfo(); // 'Name: Dale'

대신 Object 클래스의 setPrototypeOf() 정적 메서드를 사용하여 객체의 프로토타입을 안전하게 변경할 수 있습니다.

const person = {};
Object.setPrototypeOf(person, Person.prototype); // ✅
person.name = "Dale";
person.getInfo(); // 'Name: Dale'

그런데 이렇게 이미 생성된 객체의 프로토타입을 이런 식으로 변경한다는 것은 해당 객체의 상속 체계에 큰 영향을 주기 때문에 흔히 필요한 작업이 아니며 브라우저에 따라서 성능 이슈를 일으킬 수 있습니다. 따라서 가급적 객체를 생성할 시점에 프로토타입을 설정해주는 것이 더 바람직한 코딩 관례로 여겨집니다.

마치며

이상으로 자바스크립트에서 객체의 프로토타입을 제어하는 다양한 방법에 대해서 살펴보았습니다.

객체의 __proto__ 비밀 속성에 저장되는 프로토타입을 Object.getPrototypeOf()Object.setPrototypeOf() 메서드를 통해서 웹 표준에 맞게 읽고 쓰는 방법을 배웠습니다. 프로토타입 체이닝이나 쉐도잉 같은 심화 개념이 궁금하다면 자바스크립트의 프로토타입 제대로 이해하기를 참고하세요.

This work is licensed under CC BY 4.0 CC BY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord