자바스크립트는 프로토타입(Prototype) 기반 언어이다.
클래스 기반 언어에서는 '상속'을 사용하지만 프로토타입 기반 언어에서는 어떤 객체를 원형(Prototype)으로 삼고 이를 복제(참조)함으로써 상속과 비슷한 효과를 얻는다.
var instance = new Constructor();
Constructor
를 new 연산자와 함께 호출instance
생성__proto__
라는 프로퍼티 자동 부여prototype
이라는 프로퍼티 참조prototype
은 객체이며 이를 참조하는 __proto__
역시 객체
prototype
객체 내부에는 instance가 사용할 메서드를 저장하고, 그러면 인스턴스의 숨겨진 프로퍼티인 __proto__
를 통해 이 메서드들에 접근할 수 있게 된다.
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
에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있다!
생성자 함수의 프로퍼티인 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.__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