상세 컨텐츠

본문 제목

프로토타입 (Prototype)

JavaScript & TypeScript

by Yoonsang's Log 2022. 3. 1. 01:44

본문

자바스크립트는 명령형, 함수형, 프로토타입 기반 객체지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어이다.

자바스크립트의 클래스(ES6 이후)는 프로토타입 기반 패턴을 활용해서 클래스 기반 객체지향 언어(C++, JAVA 등)를 사용하는 것처럼 제공된다.

클래스에 관한 내용은 클래스 포스팅에서 자세히 다루고,

오늘은 클래스 기반 객체지향 언어보다 훨씬 강력한 객체지향 프로그래밍 능력을 가진 프로토타입이 무엇인지 살펴보자.

그리고 어떠한 프로퍼티를 검색하면 MDN 등에서 Object.prototype.~~ 과 같이 prototype 객체 내부에 있는 프로퍼티를 자주 보았을 텐데 어떤 프로퍼티는 prototype 객체가 아니기도 해서 헷갈렸던 적이 있는데 그 부분도 자세히 살펴보자!


프로토타입 체인

흔히 프로토타입이라고 부르는 프로토타입 객체란 객체 간 상속을 구현하기 위해 사용된다. 

모든 객체는 프로토타입 내부 슬롯(__proto__ 접근자 프로퍼티를 통해 접근 가능)을 가지며 객체 생성 방식에 의해 프로토타입이 결정된다.

자바스크립트 엔진은 객체의 프로퍼티에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티가 없다면 __proto__ 접근자 프로퍼티가 가리키는 참조를 따라 자신의 부모역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색한다.

그 최상위에는 Object.prototype이 있으며 모든 객체는 프로토타입의 계층 구조인 프로토타입 체인으로 묶여있다. 

 

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

// 프로토타입 메서드
Person.prototype.sayHello = function () {
  console.log(`Hello, My name is ${this.name}`);
};

const me = new Person("Lee");

위와 같은 코드가 있다고 할 때, 프로토타입 체인은 아래 그림과 같다.

모던자바스크립트 딥다이브 책 그림 19-18

me.hasOwnProperty('name') // true

위와 같이 me 객체에는 없는 hasOwnProperty 메서드를 실행하려 한다면, 프로토타입 체인 상에 해당 메서드가 존재하는지 찾는다.

해당 프로퍼티는 Object.prototype 객체에 존재하기 때문에 정상적으로 true를 반환한다.

이런 방식으로 프로토타입은 프로퍼티 검색을 할 수 있고 결과적으로 상속을 구현할 수 있다.

 

주의할 점으로는,

프로토타입 체인의 종점에서도 프로퍼티를 검색할 수 없는 경우에는 undefined를 반환한다. (에러가 발생하지 않음)

자바스크립트 배열(객체로 구현되어 있음)의 Value가 존재하지 않는 인덱스를 출력한다고 하면
즉, 프로토타입 체인 상에 프로퍼티를 검색할 수 없는 경우 undefined가 출력된다. 

C, C++, JAVA와 같이 메모리 상에 해당 배열의 크기만큼 미리 할당받아 사용하는 방식과 다르게 자바스크립트는 배열이라는 타입이 존재하지 않고 객체를 활용해 구현되어 있다.

즉, 자바스크립트의 배열은 객체 타입이다. 프로토타입 체인 상의 종점은 Object.prototype 객체이다.

배열에 대해 자세한 내용은 배열 포스팅에서 다룰 예정이다.

 

프로토타입의 변경

프로토타입은 임의의 다른 객체로 변경할 수 있다.

이러한 특징을 활용하여 상속 관계에 있는 부모 객체의 프로토타입을 동적으로 변경할 수 있다.

 

생성자 함수에 의한 프로토타입 교체

생성자 함수가 생성할 객체의 프로토타입을 객체 리터럴로 교체한다면, 교체한 객체 리터럴에는 constructor 프로퍼티를 생성자 함수로 명시해주지 않는 이상 constructor 프로퍼티와 생성자 함수 간 연결이 파괴된다.

const Person = (function () {
  function Person(name) {
    this.name = name;
  }
  Person.prototype = {
    sayHello() {
      console.log(`Hello, My name is ${this.name}`);
    },
  };
  return Person;
})();

const me = new Person("Lee");

모던 자바스크립트 딥다이브 그림 19-20

인스턴스에 의한 프로토타입 교체

생성자 함수의 prototype 프로퍼티에 다른 임의의 객체를 바인딩하여 미래에 생성할 인스턴스의 프로토타입을 교체할 수 있다.

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

const me = new Person("Lee");

const parent = {
  sayHello() {
    console.log(`Hello, My name is ${this.name}`);
  },
};

Object.setPrototypeOf(me, parent);

me.sayHello(); // Hello, My name is Lee

me 객체의 프로토타입을 Object.setPrototypeOf()를 통해 parent 객체로 변경했다.

교체된 parent 객체에는 constructor 프로퍼티가 없기 때문에 constructor 프로퍼티와 생성자 함수 간 연결이 파괴된다.

me 객체의 생성자 함수는 Person.prototype 객체가 아닌 Object.prototype 객체에 위치하게 된다.

모던 자바스크립트 딥다이브 그림 19-21

생성자 함수로 프로토타입을 교체 했을때와 마찬가지로 객체 리터럴에 constructor 프로퍼티를 추가해서 생성자 함수의 prototype 프로퍼티를 재설정하면 파괴된 생성자 함수와 프로토타입 간 연결을 되살릴 수 있다.

 

정적 프로퍼티 / 메서드

이 포스팅 초반부에 설명한 Object.prototype.~~ 과 같은 형태의 프로퍼티 혹은 메서드를 많이 보았을 텐데,

어떤 프로퍼티는 prototype 객체에 있는 것이 아닌 것을 본 적이 있을 것이다.

 

prototype 객체에 있는 프로퍼티/메서드와 그냥 객체 상에 있는 프로퍼티/메서드와의 차이는 무엇일까?

 

우선 객체 상에 있는 프로토타입 체인 상에 위치하지 않는 프로퍼티를 정적 프로퍼티 / 메서드라고 하는데,

인스턴스를 생성하지 않고도 참조 및 호출을 할 수 있는 프로퍼티/메서드를 의미한다.

 

아래 코드는 Person 함수 객체 자체에 staticProp(정적 프로퍼티)과 staticMethod(정적 메서드)를 추가해서 prototype 메서드와 차이를 알아보는 코드이다.

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

Person.prototype.sayHello = function () {
  console.log(`Hello, My name is ${this.name}`);
};

Person.staticProp = "static prop";

Person.staticMethod = function () {
  console.log("static method");
};

const me = new Person("Lee");

console.log(Person.staticProp); // static method
Person.staticMethod(); // static prop

// me.staticMethod(); // Uncaught TypeError: me.staticMethod is not a function

책에서 나오는 그림과 함께 살펴보면 이해하기 편하다.

모던 자바스크립트 딥다이브 그림 19-24

정적 프로퍼티/메서드는 인스턴스를 통해 호출할 수 없다.

프로토타입 체인 상에 위치하지 않기 때문인데, 정적 프로퍼티는 Person 함수 객체 자체에 위치하게 된다.

 

정적 프로퍼티와 프로토타입 프로퍼티를 잘 구분해서 사용하도록 하자.

 

정리

  • 자바스크립트는 클래스 기반 객체지향보다 훨씬 강력한 프로토타입 객체 지향 언어이다.
  • 모든 객체는 프로토타입 체인 상에 위치하며, 프로토타입 체인 상에 위치하는 프로퍼티 혹은 메서드 검색이 가능하며 상속을 구현한다.
  • 프로토타입은 동적으로 교체가 가능하다.
  • 정적 프로퍼티는 프로토타입 체인상에 위치하지 않기 때문에 인스턴스를 통해 호출할 수 없다.

 

 

 

[참고 자료]

- 모던 자바스크립트 딥다이브 책

- MDN

https://developer.mozilla.org/ko/docs/Learn/JavaScript/Objects/Object_prototypes

 

Object prototypes - Web 개발 학습하기 | MDN

Javascript에서는 객체를 상속하기 위하여 프로토타입이라는 방식을 사용합니다. 본 문서에서는 프로토타입 체인이 동작하는 방식을 설명하고 이미 존재하는 생성자에 메소드를 추가하기 위해

developer.mozilla.org

 

관련글 더보기

댓글 영역