코어 자바스크립트 6장을 통해 프로토타입에 대해 알아보자.
자바스크립트는 프로토타입 기반 언어이다. 어떤 객체를 원형으로 삼고 이를 복제(참조)함으로써 상속과 비슷한 효과를 얻는다.
프로토타입 도식은 다음과 같다.

var instance = new Constructor();
위 예시 코드를 도식에 맞춰 설명하면 다음과 같다.
__proto__ 프로퍼티 자동 부여__proto__는 생성자 함수(Constructor)의 prototype 프로퍼티 참조prototype과 __proto__ 의 관계가 프로토타입 개념의 핵심이다.
prototype 내부에는 인스터스가 사용할 메서드를 저장하는데, 인스턴스에서도 숨겨진 프로퍼티인 __proto__를 통해 이 메서드들에 접근할 수 있게 된다.
__proto__=> prototype => 메서드
예시를 통해 살펴보자.
var Person = function(name) {
this._name = name;
};
Person.prototype.getName = function() {
return this._name;
};
Person 인스턴스는 __proto__ 를 통해 getName을 호출할 수 있다. (__proto__가 Person의 prototype을 바라보고 있기 때문이다.)
var Person = function(name) {
this.name = name;
};
Person.prototype.getName = function() {
return this.name;
};
var heeje = new Person('heeje');
console.log(heeje.getName()); // heeje
__proto__는 생략 가능한 프로퍼티이다.
프로토타입 개념에 대한 상세한 설명
- 자바스크립트는 함수에 자동으로 prototype 프로퍼티(객체) 생성
- 함수를 생성자 함수로서 사용할 경우 인스터스에는
__proto__프로퍼티가 자동 생성되고 생성자 함수의 prototype 프로퍼티 참조
따라서, 인스터스에서 생성자 함수의 prototype 내 메서드, 프로퍼티에 접근할 수 있게 된다.
내장 생성자 함수 Array 예시)

__proto__ 는 현재 deprecated 되었다. (MDN, 직접 접근하지 말자)
생성자 함수의 프로퍼티인 prototype 객체 내부에는 constructor라는 프로퍼티가 존재한다.
constructor는 원래의 생성자 함수(자기 자신)를 참조한다.
constructor를 통해 인스턴스로부터 원형이 무엇인지 알 수 있다.
var arr = [1, 2];
Array.prototype.constructor === Array; // true
arr.__proto__.constructor === Array; // true
arr.constructor === Array; // true __proto__ 생략
var arr2 = new arr.constructor(3, 4);
console.log(arr2); // [3, 4]
[다양한 constructor 접근 방법]
var Person = function(name) {
this.name = name;
};
var p1 = new Person('사람1'); // Person { name: "사람1" } true
var p1Proto = Object.getPrototypeOf(p1);
var p2 = new Person.prototype.constructor('사람2'); // Person { name: "사람2" } true
var p3 = new p1Proto.constructor('사람3'); // Person { name: "사람3" } true
var p4 = new p1.__proto__.constructor('사람4'); // Person { name: "사람4" } true
var p5 = new p1.constructor('사람5'); // Person { name: "사람5" } true
[p1, p2, p3, p4, p5].forEach(function(p) {
console.log(p, p instanceof Person);
});
Object.getPrototypeOf()를 통해 자신이 바라보고 있는 prototype를 가지고 올 수 있다.
인스터스가 참조하고 있는 prototype 객체 내의 메서드 및 프로퍼티를 동일한 이름으로 가지고 있으면 어떻게 될까?
예시 코드를 통해 살펴보자.
var Person = function(name) {
this.name = name;
};
Person.prototype.getName = function() {
return this.name;
};
var iu = new Person('지금');
iu.getName = function() {
return '바로 ' + this.name;
};
console.log(iu.getName()); // 바로 지금
iu 객체 내에 있는 getName 메서드가 호출되는 것을 확인할 수 있다.
이는 메서드 오버라이드가 발생해서 그렇다. 기존 메서드 위에 메서드를 덮어 씌운 것이다.
자바스크립트 엔진이 getName 메서드를 찾는 방식은 다음과 같다.
__proto__ 에서 검색이 있다면 메서드 호출Person의 prototype 내 getName은 유지되는 것을 확인할 수 있다.


__proto__ 내에 __proto__ 가 등장하는 것을 확인할 수 있다.
모든 객체의 __proto__에는 Object.prototype이 연결된다. (prototype이 객체이기 때문이다.)
Array 객체를 예시로 든 최상위 객체 Object와의 구조 도식

따라서 배열은 Object.prototype 내부의 메서드를 호출하여 사용할 수 있다.
var arr = [1, 2];
arr.push(3);
arr.hasOwnProperty(2); // true
이처럼 __proto__ 내부에 다시 __proto__ 프로퍼티가 연쇄적으로 이어진 것을 프로토타입 체인이라고 한다.
그리고 이 체인을 따라 검색하는 것을 프로토타입 체이닝이라고 한다.
어떤 생성자 함수이든 prototype은 반드시 객체이기 때문에 Object.prototype이 언제나 프로토타입 체인의 최상단에 존재한다.
따라서 객체에서만 사용할 메서드를 Obejct.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"]]
['boolean', false], // []
['func', function() {}], // []
['array', [1, 2, 3]], // [["0", 1], ["1", 2], ["2", 3]]
];
data.forEach(function(datum) {
console.log(datum[1].getEntries());
});
객체만을 대상으로 동작하는 Object 전용 메서드들은 부득이 Object.prototype이 아닌 Object에 정적(static)메서드로 구현되어 있다.
반대로, Object.prototype에는 어떤 데이터에서도 활용할 수 있는 범용적인 메서드들만 있다.
toString, hasOwnProperty, valueOf, isPrototypeOf 등은 모든 instance가 직접 호출할 수 있다.
예외로 Object.create(null)는
__proto__가 없는 객체를 생성한다.
자바스크립트의 기본 내장 데이터 타입들은 모두 프로토타입 체인 1단계(객체), 2단계(나머지)로 끝나는 경우가 있지만 사용자가 새롭게 만드는 경우 계속해서 단계를 추가할 수 있다.
__proto__를 연결하는 방법은 __proto__가 가리키는 대상을 생성자 함수의 prototype이 연결하고자 하는 상위 생성자 함수의 instance를 지정해 주면된다.
var Grade = function() {
var args = Array.prototype.slice.call(arguments);
for (var i = 0; i < args.length; i++) {
this[i] = args[i];
}
this.length = args.length;
};
var g = new Grade(100, 80)
g.__proto__ 즉, Grade.prototype이 배열의 instance를 바라보게 해주면 배열의 메서드를 사용할 수 있다.Grade.prototype = [];스코프 체인과 프로토타입 체인의 관계에 대해...
https://homoefficio.github.io/2016/01/16/JavaScript-%EC%8B%9D%EB%B3%84%EC%9E%90-%EC%B0%BE%EA%B8%B0-%EB%8C%80%EB%AA%A8%ED%97%98/