Logo

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

자바스크립트는 프로토타입(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() 메서드를 통해서 웹 표준에 맞게 읽고 쓰는 방법을 배웠습니다.