[Javascript] [[Prototype]]

bluewhale·2021년 9월 5일
0

Javascript

목록 보기
1/3
post-thumbnail

이 글은 이응모 님의 5.14 Prototype 글을 읽고 정리한 내용입니다.

[[Prototype]]

자바스크립트는 프로토타입 기반의 객체지향형 프로그래밍 언어이다. 자바스크립트의 모든 객체는 [[Prototype]]이라는 숨겨진 속성(internal slot)을 갖고 있다. [[Prototype]]은 항상 null이거나 다른 객체에 대한 참조를 값으로 갖는다. 만약 [[Prototype]]null이 아닌 경우, 참조 대상을 해당 객체의 Prototype 객체라고 부른다. 자바스크립트에서는 하나의 객체는 하나의 프토토타입만을 가질 수 있다.

proto와 getPrototypeOf

자바스크립트에서 객채의 [[Prototype]]에 접근하는 방법은 크게 2가지가 있다.

  1. __proto__
    __proto__는 객체의 [[Prototype]] 속성에 대한 getter이자 setter에 해당한다. __proto__는 브라우저 환경에서만 지원하도록 자바스크립트 명세서에서 정의되어 있는데, 대부분의 서버 사이드 런타임(ex, nodejs)에서도 이를 지원한다. 그러나, 모던 자바스크립트에서는 아래에 설명할 Object.getPrototypeOf(), Object.setPrototypeOf() 메서드의 사용을 권장한다.

  2. Object.getPrototypeOf()
    객체의 [[Prototype]]을 반환하는 함수이다. 객체의 프토로타입을 수정하는 Object.setPrototypeOf()도 함께 지원한다. 런타임에 객체의 [[Prototype]]을 수정하는 것은 자바스크립트 엔진의 객체 속성 접근 관련 최적화에 영향을 주어, 성능 저하를 발생시킬 수 있으므로 지양하는 것이 좋다.

Object.prototype

Object.prototype 객체는 Object 생성자 함수에 의해 생성된 객체의 프로토타입 객체로, 모든 객체의 프로토타입 체인 끝에 위치하는 특수한 객체이다. 따라서, 함수 객체를 포함한 모든 객체는 Object.prototype 객체에 포함된 속성과 메서드에 접근할 수 있다.

Prototype의 작동 원리

프로토타입은 일반적인 OOP의 상속과 유사하게 동작한다. 예를 들어, 런타임에 B 객체에 존재하지 않는 속성이나 메서드를 호출할 경우, 자바스크립트에서는 자동으로 B 객체의 [[Prototype]]에서 해당 속성 혹은 메서드를 탐색하여 반환한다.

추가적으로, 객체 리터럴로 선언된 모든 객체는 자바스크립트 엔진에 의해 내부적으로Object 생성자 함수로 치횐된다. 따라서, 객체 리터럴로 생성된 인스턴스는 모두 Object.prototype 객체를 프로토타입으로 갖는다. 즉, 아래의 예시에서 animal 객체의 프로토타입은 Object.prototype 객체이다.

// 
let animal = {
  eats: true
  // __proto__: Object.prototype, 
}

let tiger = {
  jumps: true,
  __proto__: animal // equivalent to rabbit.setPrototypeOf(animal)
}

console.log(animal.__proto__ === Object.prototype) // true
console.log(tiger.eats) // true, equivalent to tiger.__proto__.eats
console.log(tiger.jumps) // true
console.log(animal.jumps) // undefined

자바스크립트에서는 하나의 객체는 하나의 프로토타입만을 가질 수 있다. 그러나, 자식 객체에서는 부모 객체의 프로토타입에 접근하는 것이 가능하다. 이를 활용하면 길게 연결된 상속 관계, 프로토타입 체인을 만드는 것도 가능하다.

let animal = {
  eats: true
}

let tiger = {
  jumps: true,
  __proto__: animal 
}

let siberianTiger = { // 시베리아 호랑이
  endureCold: true,
  __proto__: tiger,
}

console.log(siberianTiger.eats) // true, equivalent to siberianTiger.__proto__.__proto__eats

[[Prototype]] vs prototype 속성

[[Prototype]] 속성은 모든 객체가 갖고 있다. [[Prototype]]은 객체의 상속 관계에서 자신의 부모에 해당하는 객체를 가리킨다. 특징적으로, 자바스크립트에서는 모든 함수가 Function() 생성자 함수를 통해서 생성되기 때문에, 모든 함수 객체의 [[Prototype]]Function.prototype을 가리킨다.

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

console.log(Animal.__proto__ === Function.prototype) /// true

반면, prototype 속성은 함수 객체에만 존재하는 특수한 속성으로, 해당 함수 객체를 생성자로 하여 생성된 객체의 프로토타입 객체를 가리킨다.

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

let tiger = new Animal("tiger")

console.log(tiger.__proto__ === Animal.prototype) /// true

This

자바스크립트에서 this는 항상 해당 메서드를 호출한 대상 객체를 가리킨다. 프로토타입에서 상속받은 메서드나 할지라도 이러한 성질은 변하지 않는다. 아래 예시에서, tiger.eat()를 호출한 경우, 메서드 내부의 this는 해당 메서드를 호출한 tiger 객체를 가르킨다. 따라서, 메서드가 실질적으로 선언된 animal 객체가 아니라, tiger 객체의 속성의 값이 바뀌게 된다.

let animal = {
  isHungry: true,
  eat() {
    this.isHungry = false;
  }
}

let tiger = {
  __proto__: animal
}

tiger.eat()
console.log(tiger.isHungry) // false
console.log(animal.isHungry) // true

References

프로토타입 상속
상속과 프로토타입

profile
안녕하세요

0개의 댓글