처음부터 차근차근

[코어 자바스크립트] Prototype 본문

Language/JavaScript

[코어 자바스크립트] Prototype

HangJu_95 2023. 11. 13. 15:19
728x90

Prototype의 사전적 의미

prototype : 무언가 제품을 만드는 과정에서 시험용으로 미리 만들어보는 물건을 의미.

※ 시제품이나, Test를 진행하기 위한 Prototype 제작 등

Javascript에서 Prototype 의미

  • 자바스크립트는 프로토타입 기반 언어이다.
  • 프로토타입 기반 언어에서는 어떤 객체를 원형으로 삼고 이를 복제(참조)함으로써 상속과 비슷한 효과를 얻는다.

ES5까지 자바스크립트는 Class라는 개념이 없었다. 기본적으로 프로토타입 언어이기 때문에 어떠한 Object를 원형(시제품)으로 삼고 이를 복제함으로써 Class와 비슷한 효과를 얻었다.

 

먼저 예제와 도식을 보자.

var instance = new Constructor();

예시와 프로토타입 도식

  • 어떤 생성자 함수(Constructor)를 new 연산자와 함께 호출하면
  • Constructor에서 정의된 내용을 바탕으로 새로운 instance가 생성된다.
  • 이때 instance에는 __proto_-라는 프로퍼티가 자동으로 부여되는데
  • 이 프로퍼티는 Constructor의 prototype이라는 프로퍼티를 참조한다.
여기서 중요한 것은 prototype이라는 프로퍼티와 __proto__라는 프로퍼티가 새로 등장했는데, 이 둘의 관계가 프로토타입 개념의 핵심이다.
  • prototype은 객체이며, 객체 내부에는 인스턴스가 사용할 메서드를 저장한다.
  • Instance의 숨겨진 프로퍼티인 __proto__를 통해 이 메서드들에 접근할 수 있게 된다.
var Person = function(name) {
  this._name = name;
};

// prototype에 메서드 추가
Person.prototype.getName = function() {
  return this._name;
};

// 새로운 인스턴스 생성
var suzi = new Person('Suzi')
// this가 __proto__로 바인딩 된다.
// __proto__에는 _name 속성이 없으니, undefined가 출력!
suzi.__proto__.getName(); // undefined

// instance의 __proto__와 Person의 prototype이 같은 객체를 바라본다.
Person.prototype === suzi.__proto__ // true
  • __proto__는 생략 가능한 프로퍼티이다.

__proto__를 생략하지 않으면 this는 suzi.__proto__를 가리키지만, 이를 생략하면 suzi를 가리킨다. suzi.__proto__에 있는 메서드인 getName을 실행하지만, this 는 suzi를 바라보게 할 수 있게 된다.

var suzi = new Person('Suzi');
suzi.__proto__.getName(); // undefined
suzi.getName(); // Suzi

도식을 보면 다음과 같아지게 된다.

 

즉, 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면, 인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있게 된다.

 

크롬 개발자 도구를 통해 다른 예시를 보자.

var Constructor = function(name) {
  this.name = name;
};
Constructor.prototype.method1 = function() {};
Constructor.prototype.property1 = 'Constructor Prototype Property';

var instance = new Constructor('Instance');
console.dir(Constructor);
console.dir(instance);

'Constructor Prototype Property'를 prototype에서 확인할 수 있다.

또한 생성자 함수의 인스턴스는 해당 생성자 함수의 이름을 표기함으로써 해당 함수의 인스턴스임을 보이고 있다.

이러한 부분은 Array 또한 마찬가지로써, 우리가 배열을 하나 생성하면 push, pop, forEach등을 메서드로 사용할 수 있는 이유가 바로 프로토타입 덕분이라고 할 수 있다.

Constructor 프로퍼티

생성자 함수의 프로퍼티인 prototype 객체 내부에는 constructor라는 프로퍼티가 있으며, 인스턴스의 __proto__객체 내부에도 마찬가지로 존재한다.

constructor 프로퍼티는 단어 그대로 원래의 생성자 함수(자기 자신)을 참조한다.

var arr = [1, 2];
Array.prototype.constructor === Array; // true
arr.__proto__.constructor === Array; // true
arr.constructor === Array; // true

// arr 인스턴스의 __proto__가 생성자 함수의 prototype 프로퍼티를 참조하며
// __proto__가 생략 가능하기 때문에 인스턴스에서 직접 constructor에 접근할 수 있다.
var arr2 = new arr.constructor(3, 4);
console.log(arr2); // [3, 4]
  • constructor는 읽기 전용 속성이 부여된 예외적인 경우(기본형 리터럴 변수 - String, number, boolean)을 제외하고 값을 변경할 수 있다.
  • 그러나 constructor를 변경하더라도 참조하는 대상이 변경될 뿐, 이미 만들어진 인스턴스의 원형이 바뀐다거나 데이터 타입이 변하는 것은 아니다.

코드를 통해 정리하자면

// 1. 다음 각 줄은 모두 동일한 대상(constructor)을 가리킨다.
[Constructor]
[instance].__proto__.constructor
[instance].constructor
Object.getPrototypeOf([instance]).constructor
[Constructor].prototype.constructor

// 2. 다음 각 줄은 모두 동일한 객체(prototype)에 접근할 수 있다
[Constructor].prototype
[instance].__proto__
[instance]
Object.getPrototypeOf([instance])

 

프로토타입 체인

프로토타입 체인 : 어떤 데이터의 __proto__내부에 다시 __proto__프로퍼티가 연쇄적으로 이어진 것을 의미한다.

프로토타입 체이닝 : 프로토타입 체인을 따라가며 검색하는 것을 의미

 

어떤 메서드를 호출하면 JS 엔진은 데이터 자신의 프로퍼티를 검색해서 원하는 메서드가 있으면 그 메서드를 실행하고, 없으면 __proto__를 검색해서 있으면 그 메서드를 실행하고, 없으면 또 다시 __proto__를 검색해서 실행하는 식으로 진행한다.

 

코드 예시를 통해 확인하자.

var arr = [1, 2];
// 배열 메서드
arr.push(3);
// 객체 메서드
arr.hasOwnProperty(2); // true
  • 여기서는 배열의 메서드와 객체의 메서드를 둘 다 사용하였다.
  • 객체의 메서드를 사용할 수 있는 이유는, Array.prototype은 객체이며, 즉 이것은 object.prototype을 참조하고 있기 때문에, 프로토타입 체이닝에 의해 사용할 수 있는 것이다.

메서드 오버라이드

var Person = function(name) {
  this.name = name;
};
Person.prototype.getName = function() {
  return this.name;
};

var iu = new Person('지금');
// getName이라는 동일한 메서드 추가
iu.getName = function() {
  return '바로 ' + this.name;
};
console.log(iu.getName()); // 바로 지금
  • 여기서는 메서드 오버라이드 현상이 발생한다.
  • 메서드 위에 메서드를 덮어씌운다는 표현이 적합하며, 원본이 그대로 있는 상태에서 다른 대상을 그 위에 얹는 이미지를 떠올리면 정확하다.

객체 전용 메서드의 예외사항

  • 어떤 생성자 함수이든 prototype은 반드시 객체이기 때문에 Object.prototype이 언제나 프로토타입 체인의 최상단에 존재한다.
  • 따라서 객체에서만 사용할 메서드는 다른 여느 데이터 타입처럼 프로토타입 객체 안에 정의할 수 없다.
  • 객체 전용 메서드들은 부득이하게 object에 static method로 부여할 수 밖에 없다.

'Language > JavaScript' 카테고리의 다른 글

[Deep Dive] 37. Set과 Map  (0) 2023.12.15
[Deep Dive] 6. 데이터 타입  (0) 2023.10.26
[Deep Dive] 5. 표현식과 문  (0) 2023.10.26
[Deep Dive] 4. 변수  (1) 2023.10.26
[Javascript] 자바스크립트 동작 원리  (1) 2023.10.25