[자바스크립트] 프로토타입(Prototype)

minidoo·2020년 11월 30일
0

자바스크립트 / NodeJS

목록 보기
20/28
post-thumbnail

자바스크립트는 프로토타입(Prototype) 기반 언어이다.

클래스 기반 언어에서는 '상속'을 사용하지만 프로토타입 기반 언어에서는 어떤 객체를 원형(Prototype)으로 삼고 이를 복제(참조)함으로써 상속과 비슷한 효과를 얻는다.

프로토타입의 개념

var instance = new Constructor();
  • 어떤 생성자 함수 Constructor 를 new 연산자와 함께 호출
  • Constructor에서 정의된 내용을 바탕으로 새로운 instance 생성
  • 이때 instance에는 __proto__ 라는 프로퍼티 자동 부여
  • 이 프로퍼티는 Constructor의 prototype 이라는 프로퍼티 참조

prototype객체이며 이를 참조하는 __proto__ 역시 객체

prototype 객체 내부에는 instance가 사용할 메서드를 저장하고, 그러면 인스턴스의 숨겨진 프로퍼티인 __proto__ 를 통해 이 메서드들에 접근할 수 있게 된다.

메서드 호출 시 this

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

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

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

miniddo.__proto__.getName() 에서 getName 함수 내부에서의 this는 miniddo.__proto__ 이다. 이 객체 내부에는 name 프로퍼티가 없으므로 undefined를 반환한다.

var miniddo = new Person('miniddo');
miniddo.__proto__._name = 'miniddo';
miniddo.__proto__.getName();		// miniddo

__proto__는 생략 가능

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

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

var namu = new Person('namu');
console.log(namu.getName());		// namu

var coffee = new Person('coffee');
console.log(coffee.getName());		// coffee

__proto__ 프로퍼티는 생략 가능하도록 구현돼 있기 때문에, 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있다!

constructor 프로퍼티

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

이 프로퍼티는 단어 그대로 원래의 생성자 함수(자기 자신)을 참조한다.
대표적인 내장 생성자 함수인 Array의 예시이다.

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

프로토타입 체인

console.dir({ a:1 });

__proto__ 안에는 또 다시 __proto__ 가 들어있다.

안을 열어보면 밖의 __proto__ 와 동일한 내용으로 이뤄져 있다.
이유는 prototype 객체가 객체이기 때문이다. 모든 객체의 __proto__ 에는 Object.prototype 이 연결된다.

대표적인 내장 생성자 함수인 Array로 예를 들어보자.

var arr = [1, 2];
  • arr 변수는 배열이므로 arr.__proto__Array.prototype 을 참조한다.
  • Array.prototype 은 객체이므로 Array.prototype.__proto__Object.prototype 을 참조한다.

프로토타입 체인 (prototype chain) : 어떤 데이터의 __proto__ 프로퍼티가 연쇄적으로 이어진 것을 말한다.
프로토타입 체이닝 (prototype chaining) : 이 체인을 따라가며 검색하는 것을 말한다. ( __proto__ 안에 다시 __proto__를 찾아가는 과정 )


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

Object.prototype 은 언제나 프로토타입 체인의 최상단에 존재한다.
따라서 객체에서만 사용할 메서드는 프로토타입 객체 안에 정의할 수 없다. 객체에서만 사용할 메서드를 Object.prototype 내부에 정의한다면 다른 데이터 타입도 해당 메서드를 사용할 수 있게 되기 때문이다.

Object.prototype.getEntries = function() {
    var res = [];
    for(var prop in this) {
        if (this.hasOwnProperty(prop)) {
            res.push([prop, this[prop]);
        }
    }
    
    return res;
};

var data = [
    ['object', { a:1, b:2, c:3 }],		// [['a',1], ['b',2], ['c',3]]
    ['number', 345],				// []
    ['string, 'abc'],				// [['0', 'a'], ['1', 'b'], ['2', 'c']]
    ['array', [1, 2, 3]]			// [['0', 1], ['1', 2], ['2', 3]]
];

data.forEach(function(data) {
    console.log(data[1].getEntries());
});

위 예제에서 객체에만 사용할 의도로 getEntries 라는 메서드를 만들었다.
forEach로 data의 각 줄마다 getEntries를 실행했더니, 모든 데이터가 오류 없이 결과를 반환하고 있다.

원래 의도대로라면 객체가 아닌 타입(number, string, array)에 대해서는 오류를 반환해야하지만, 프로토타입 체이닝을 통해 getEntries 메서드에 접근할 수 있어서 그렇게 동작하지 않는 것이다.

이와 같은 이유로 객체만을 대상으로 동작하는 객체 전용 메서드들은 Object.prototype이 아닌 Static Method 로 부여한다.


참고 서적

코어 자바스크립트(Core JavaScript)
https://book.naver.com/bookdb/book_detail.nhn?bid=15433261

0개의 댓글