프로토타입 기반 언어
클래스 기반 언어
var instance = new Constructor();
- ES5.1 명세에는
__prototype__가 아니라[[prototype]]이라는 명칭으로 정의되어있다.
__prototype__라는 프로퍼티는 사실 브라우저들이[[prototype]]을 구현한 대상에 지나지 않는다.
- 명세에는
instance.__prototype__와 같은 방식으로 직접 접근하는 것은 허용하지 않고
오직Object.getPrototypeOf(instance)/Reflect.getPrototypeOf(instance)를 통해서만 접근할 수 있도록 정의
- 이런 명세에도 불구하고 대부분 브라우저들이
__prototype__에 직접 접근하는 방식을 포기하지 않아
결국 ES6에서는 이를 브라우저에서 동작하는 레거시 코드에 대한 호환성 유지 차원에서 정식으로 인정하기에 이르렀다.
- 다만 브라우저에서의 호환성을 고려한 지원일 뿐 권장하는 방식은 아니라
브라우저가 아닌 다른 환경에서는 이 방식이 지원되지 않을 수도 있다.
- 실무에서는 가급적
__prototype__를 사용하지 말고
Object.getPrototypeOf()/Object.create()등을 이용하도록 하자.
Person이라는 생성자 함수의 prototype에 getName이라는 메서드 지정한다.
var Person = function (name) {
this._name = name;
};
Person.prototype.getName = function() {
return this._name;
};
이제 Person의 인스턴스는 __proto__ 프로퍼티를 통해 getName을 호출할 수 있다.
var suzi = new Person('Suzi');
suzi.__proto__.getName(); // undefined
왜냐하면 instance의 __proto__가 Constructor의 prototype 프로퍼티를 참조하므로
결국 둘은 같은 객체를 바라보기 때문이다.
Person.prototype === suzi.__proto__ // true
undifined가 나왔다는 것은 이 변수가 호출할 수 있는 함수에 해당한다.
만약 함수가 아닌 다른 데이터 타입이었다면 TypeError 발생한다.
함수내부에서 this.name 값 리턴하는데
this에 바인딩된 대상이 잘못 지정됐다.
어떤 함수를 '메서드로서' 호출할 때는 메서드명 바로 앞의 객체가 곧 this가 된다.
thomas.__proto__.getName()에서 getName 함수 내부에서의 this는 thomas가 아니라
thomas.__proto__라는 객체가 된다.
이 객체 내부에는 name 프로퍼티가 없으므로
찾고자 하는 식별자가 정의되어 있지 않을 때는 Error 대신 undefined를 반환한다
라는 자바스크립트 규약에 의해 undefiend가 반환되었다.
만약 __proto__ 객체에 name 프로퍼티가 있다면?
var suzi = new Persin('Suzi');
suzi.__proto__._name = 'SUZI__proto__';
suzi.__proto__.getName(); // SUZI__proto
SUZI__proto__ 잘 출력된다.
관건은 this
this를 인스턴스로 할 수 있는 방법: __proto__ 없이 인스턴스에서 곧바로 메서드 쓰기
var suzi = new Person('Suzi', 28);
suzi.getName(); // Suzi
var iu = new Person('Jieun', 28);
iu.getName(); // Jieun
__proto__를 빼면 this는 instance가 되는 것이 맞지만,
이대로 메서드가 호출되고 원하는 값이 나오는건 이상해보임에도 정상이다.
__proto__가 생략 가능한 프로퍼티이기 때문이다.
suzi.__proto__.getName
-> suzi(.__proto__).getName
-> suzi.getName
정리하자면 ~
__proto__를 생략하지 않으면 this는 suzi.__proto__를 가리키지만,
이를 생략하면 suzi를 가리킨다.
suzi.__proto__에 있는 메서드인 getName을 실행하지만 this는 suzi를 바라보게 할 수 있게 된다.

new연산자로Constructor를 호출하면instance가 만들어지는데,
이instance의 생략 가능한 프로퍼티인__proto__는Constructor의prototype을 참조한다.
자바스크립트는 함수에 자동으로 객체인 prototype 프로퍼티를 생성해 놓는데,
해당 함수를 생성자 함수로서 사용할 경우, 즉 new 연산자와 함께 호출할 경우,
그로부터 생성된 인스턴스에는 숨겨진 프로퍼티인 __proto__가 자동으로 생성되며,
이 프로퍼티는 생성자 함수의 prototype 프로퍼티를 참조한다.
__proto__ 프로퍼티는 생략 가능하도록 구현되어 있기 때문에
생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면
인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있게 된다.
prototype과 __proto__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의 디렉토리 구조 출력 결과첫줄: 함수
f이름Constructor인자name
내부:argumentscallerlengthnameprototype__proto__
prototype:method1property1constructor__proto__
색상 차이는
{ enumerable: false }속성이 부여된 프로퍼티인지 여부에 따른다.
짙은 색 enumerable 열거 가능한 프로퍼티
옅은 색 innumerable 열거할 수 없는 프로퍼티
for in등으로 객체의 프로퍼티 전체에 접근하고자 할 때 접근 가능 여부를 색상으로 구분지어 표기
instance의 디렉토리 구조 출력 결과출력 결과에는
Constructor나오고 있다.
어떤 생성자 함수의 인스턴스는 해당 생성자 함수의 이름을 표기함으로써
해당 함수의 인스턴스임을 표기하고 있다.
Constructor와 동일한 내용으로 구성
var arr = [1, 2];
console.dir(arr);
console.dir(Array);
첫줄: Array(2)
Array라는 생성자 함수를 원형으로 삼아 생성됐고, length가 2이다.
인덱스 0, 1 ,length__proto__
__proto__에는 배열에 사용하는 거의 모든 메서드 들어있다.
pushpopshiftunshiftslicespliceconcatfindfilterforEachmapsortindexOfeverysome...
첫줄: 함수
f
기본 프로퍼티argumentscallerlengthname
Array 함수 정적 메서드fromisArrayof
prototype에는 변수 arr의__proto__과 완전히 동일한 내용으로 구성되어있다.
Array를 new 연산자와 함께 호출해서 인스턴스를 생성하든, 배열 리터럴을 생성하든,
instance인 [1, 2]가 만들어진다.
이 인스턴스의 __proto__은 Array.prototype을 참조하는데,
__proto__가 생략 가능하도록 설계되어 있기 때문에
인스턴스가 push pop forEach 등의 메서드를 자신의 것처럼 호출할 수 있다.
한편 Array의 prototype 프로퍼티 내부에 있지 않은 from, isArray 등의 메서드들은 인스턴스가 직접 호출할 수 없다.
이들은 Array 생성자 함수에서 직접 접근해야 실행할 수 있다.
var arr = [1, 2];
arr.forEach(function (){}); // (O)
Array.isArray(arr); // (O) true
arr.isArray(); // (X) TypeError: arr.isArray is not a function
생성자 함수의 프로퍼티인 prototype 객체 내부에는 constructor라는 프로퍼티가 있다.
인스턴스의 __proto__ 객체 내부에도 constructor라는 프로퍼티가 있다.
constructor 프로퍼티는 단어 그대로 원래의 생성자 함수(자기 자신)을 참조한다.
자신을 참조하는 프로퍼티를 굳이 뭐하러 가지고 있을까 싶지만, 이 역시 인스턴스와의 관계에 있어서 필요한 정보이다.
인스턴스로부터 그 원형이 무엇인지를 알 수 있는 수단이다.
var arr = [1, 2];
Array.prototype.constructor === Array // true
arr.__proto__.constructor === Array // true
arr.consturctor === Array // true
var arr2 = new arr.constructor(3, 4);
console.log(arr2); // [3, 4]
인스턴스의 __proto__가 생성자 함수의 prototype 프로퍼티를 참조하며 __proto__가 생략 가능하기 때문에
인스턴스에서 직접 consturctor에 접근할 수 있는 수단이 생긴다.
한편, consturctor는 읽기 전용 속성이 부여된 예외적인 경우(기본형 리터럴 변수 number string boolean)를 제외하고는 값을 바꿀 수 있다.
var NewConstructor = function () {
console.log('this is new constructor!');
};
var dataTypes = [
1, // Number & false
'test', // String & false
true, // Boolean & false
{}, // NewConstructor & false
[], // NewConstructor & false
function () {}, // NewConstructor & false
/test/, // NewConstructor & false
new Number(), // NewConstructor & false
new String(), // NewConstructor & false
new Boolean, // NewConstructor & false
new Object(), // NewConstructor & false
new Array(), // NewConstructor & false
new Function(), // NewConstructor & false
new RegExp(), // NewConstructor & false
new Date(), // NewConstructor & false
new Error() // NewConstructor & false
];
dataTypes.forEach(function (d) {
d.constructor = NewConstructor;
console.log(d.constructor.name, '&', d instanceof NewConstructor);
});
모든 데이터가 d instanceof NewConstructor 명령에 대해 false를 반환한다.
이로부터 constructor를 변경하더라도 참조하는 대상이 변경될 뿐
이미 만들어진 인스턴스의 원형이 바뀐다거나 데이터 타입이 변하는 것은 아님을 알 수 있다.
어떤 인스턴스의 생성자 정보를 알아내기 위해 constructor 프로퍼티에 의존하는 게 항상 안전하지는 않다.
비록 어떤 인스턴스로부터 생성자 정보를 알아내는 유일한 수단인 constructor가 항상 안전하지는 않지만 오히려 그렇기 때문에 클래스 상속을 흉내 내는 등이 가능해진 측면도 있다. 7장에서 계속...
var Person = function (name) {
this.name = name;
};
var p1 = new Person('사람1'); // { name: "사람1" } true
var p1Proto = Object.getPrototypeOf(p1)';
var p2 = new Person.prototype.constructor('사람2'); // { name: "사람2" } true
var p3 = new p1Proto.constructor('사람3'); // { name: "사람3" } true
var p4 = new p1.__proto__.constructor('사람4'); // { name: "사람4" } true
var p5 = new p1.constructor('사람5'); // { name: "사람5" } true
[p1, p2, p3, p4, p5].forEach(function (p) {
console.log(p, p instanceof Person);
});
p1부터 p5까지 모두 Person의 인스턴스로 다음 두 공식이 성립한다.
[Constructor]
[instance].__proto__.constructor
[instance].constructor
Object.getPrototypeOf([instacne]).constructor
[Constructor].prototype.constructor
[Constructor].prototype
[instance].__proto__
[instance]
Object.getPrototypeOf([instance])
prototype 객체를 참조하는 __proto__를 생략하며 인스턴스는 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.__proto__.getName이 아닌 iu 객체에 있는 getName 메서드가 호출됐다.
메서드 위에 메서드를 덮어씌운 현상을 메서드 오버라이드라고 한다.
원본을 제거하고 다른 대상으로 교체하는 것이 아니라 원본이 그대로 있는 상태에서 다른 대상을 그 위에 얹는 것이다.
자바스크립트 엔진이 getName이라는 메서드를 찾는 방식은 가장 가까운 대상인 자신의 프로퍼티를 검색하고, 없으면 그다음으로 가까운 대상인 __proto__를 검색하는 순서로 진행된다.
__proto__에 있는 메서드는 자신에게 있는 메서드보다 검색 순서에서 밀려 호출되지 않은 것이다.
교체하는 형태라면 원본에는 접근할 수 없는 형태가 되겠지만,
얹는 형태라면 원본이 아래에 유지되고 있으니 원본에 접근할 수 있는 방법도 있다.
메서드 오버라이딩이 이뤄져 있는 상황에서 prototype에 있는 메서드에 접근하려면?
console.log(iu.__proto__.getName()); // undefined
iu.__proto__.getName을 호출했더니 undefined가 출력됐다.
this가 prototype 객체 iu.__proto__를 가리키는데 prototype 상에는 name 프로퍼티가 없기 때문이다.
만약 prototype에 name 프로퍼티가 있다면 그 값을 출력할 것이다.
Person.prototype.name = '이지금';
console.log(iu.__prototype__.getName()); // 이지금
원하는 메서드(prototype에 있는 getName)가 호출되고 있다는게 확실해졌다.
다만 this가 prototype을 바라보고 있는데 이걸 인스턴스를 바라보도록 바꿔주면 된다.
call이나 apply로 해결 가능할 것 같다.
console.log(iu.__proto__.getName.call(iu)); // 지금
일반적으로 메서드가 오버라이드된 경우에는 자신으로부터 가장 가까운 메서드에만 접근할 수 있지만, 그 다음으로 가까운 __proto__의 메서드도 우회적인 방법을 통해서이긴 하지만 접근이 불가능한 것은 아니다.
console.dir({ a: 1});
첫줄: Object의 인스턴스
프로퍼트 a: 1
__proto__내부:hasOwnPropertyisPrototypeOftoLocaleStringtoStringvalueOf메서드
constructor: 생성자 함수Object가리킴
배열 리터럴의
__proto__에는poppush등 익숙한 배열 메서드 및constructor가 있다.
이__proto__안에는 또다시__proto__가 등장한다.
prototype 객체가 '객체'이기 때문에 객체의__proto__와 동일한 내용으로 이뤄져있다.
기본적으로 모든 객체의__proto__에는Object.prototype이 연결된다.
prototype객체도 예외가 아니다.
__proto__는 생략 가능하다고 했다.
배열이 Array.prototype 내부의 메서드를 마치 자신의 것처럼 실행할 수 있다.
마찬가지로 Object.prototype 내부의 메서드도 자신의 것처럼 실행할 수 있다.
생략 가능한 __proto__를 한 번 더 따라가면 Object.prototype을 참조할 수 있기 때문이다.
var arr = [1, 2];
arr(.__proto__).push(3);
arr(.__proto__)(.__proto__).hasOwnProperty(2); // true
어떤 데이터의 __proto__ 프로퍼티 내부에 다시 __proto__ 프로퍼티가 연쇄적으로 이어진 것을 프로토타입 체인이라하고,
이 체인을 따라가며 검색하는 것을 프로토타입 체이닝이라고 한다.
var arr = [1, 2];
Array.porototype.toString.call(arr); // 1, 2
Object.prototype.toString.call(arr); // [object Array]
arr.toString(); // 1, 2
arr.toString = function () {
return this.join('_');
};
arr.toString(); // 1_2
arr 변수는 배열이므로 arr.__proto__는 Array.prototype을 참조하고,
Array.prototype은 객체이므로 Array.prototype.__prototype을 참조할 것이다.
toString이라는 이름을 가진 메서드는 Array.prototype 뿐 아니라 Object.prototype에도 있다.
이 둘 중 어떤 값이 출력되는지 확인하기 위해 우선,
2, 3번째 줄: Array, Object의 각 프로토타입에 있는 toString 메서드를 arr에 적용했을 때의 출력값을 미리 확인해 보았다.
4번째 줄: arr.toString을 실행한 결과는 Array.prototype.toString을 적용한 것과 동일하다.
6번째 줄: arr에 직접 toString 메서드를 부여하여
9번째 줄: Array.prototype.toString이 아닌 arr.toString이 바로 실행된다.
비단 배열만이 아니라, 자바스크립트 데이터는 모두 동일한 형태의 프로토타입 체인 구조를 지닌다.
Q) 위쪽 삼각형의 우측 꼭짓점에는 무조건 Object.prototype이 있는 걸까요?
A) Yes 2-3. 객체 전용 메서드의 예외사항에서 계속...
Q) 삼각형은 꼭 두 개만 연결될까요?
A) No 2-4. 다중 프로토타입 체인에서 계속...
앞서 소개한 도식의 삼각형들은 오직 instance를 중심으로 __porto__를 따라가는 루트만 표기했는데,
접근 가능한 모든 경우를 표기하면 다음 그림처럼 복잡한 구조가 되어버립니다.
각 생성자 함수는 모두 함수이기 때문에 Function 생성자 함수의 prototype과 연결된다.
Function 생성자 함수 역시 함수이므로 다시 Function 생성자 함수의 prototype과 연결된다.
이런 식으로 __proto__의 constructor의 __proto__의 constructor...를 재귀적으로 반복하는 루트를 따르면 끝없이 찾아갈 수 있다.
실제 메모리 상에서 데이터를 무한대의 구조 전체를 들고 있는 것이 아니고,
사용자가 이런 루트를 통해 접근하고자 할 때 해당 정보를 얻을 수 있을 뿐이다.
사실 instance.consturctor.constructor이든 instance.constructor.constructor.constructor이든 결국 같은 Fuction 생성자 함수를 가리키므로 메모리가 낭비될 이유는 없다.
뿐만 아니라 이런 접근은 그 자체로 별 의미가 없다.
이미 생성자 함수를 알고 있는 이상, 어떤 인스턴스가 해당 생성자 함수의 인스턴스인지 여부를 알아야 하는 경우가 아니라면 그냥 생성자 함수를 사용하면 되지 굳이 인스턴스를 통해 접근해야 할 필요는 없다.
=> 인스턴스와 "직접적인 연관"이 있는 삼각형에만 주목하면 된다.
어떤 생성자 함수이든 prototype은 반드시 객체이기 때문에 Object.prototype이 언제나 프로토타입 체인의 최상단에 존재한다.
따라서 객체에서만 사용할 메서드는 다른 여느 데이터 타입처럼 프로토타입 객체 안에 정의할 수가 없다.
객체에서만 사용할 메서드를 Object.prototype 내부에 정의한다면 다른 데이터 타입도 해당 메서드를 사용할 수 있게 되기 때문이다.
Object.prototype.getEntries = fuction() {
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());
});
1번째 줄: 객체에서만 사용할 의도로 getEntries라는 메서드를 만들었다.
18번째 줄: forEach에 따라 11번째 줄부터 16번째 줄의 각 데이터마다 getEntries를 실행해 보니, 모든 데이터가 오류 없이 결과를 반환하고 있다.
원래 의도대로라면 객체가 아닌 다른 데이터 타입에 대해서는 오류를 던지게끔 돼야 할 텐데,
어느 데이터 타입이건 거의 무조건 프로토 타입 체이닝을 통해 getEntries 메서드에 접근할 수 있으니 그렇게 동작하지 않는다.
이 같은 이유로 객체만을 대상으로 동작하는 객체 전용 메서드들은 부득이 Obejct.prototype이 아닌 Object에 스태틱 메서드로 부여할 수 밖에 없다.
또한 생성자 함수인 Object와 인스턴스인 객체 리터럴 사이에는 this를 통한 연결이 불가능하기 때문에
여느 전용 메서드처럼 '메서드명 앞의 대상이 곧 this'가 되는 방식 대신 this의 사용을 포기하고 대상 인스턴스를 인자로 직접 주입해야 하는 방식으로 구현되어 있다.
( 만약 객체 전용 메서드에 대해서도 다른 데이터 타입과 마찬가지의 규칙을 적용할 수 있다면,
예를 들어 Object.freeze(instance)의 경우 instance.freeze()처럼 표현할 수 있을 것이다.
instance.__proto__(생성자 함수의 prototype)에 freeze라는 메서드가 있었을 것이다.
또한 앞서 소개한 Object.getPrototypeOf(instance)의 경우에도 instance.getPrototype() 정도로 충분했을 것이다. )
객체 한정 메서드들을 Object.prototype이 아닌 Object에 직접 부여할 수밖에 없었던 이유는 Object.prototype이 여타의 참조형 데이터 뿐 아니라 기본형 데이터조차 __proto__에 반복 접근함으로써 도달할 수 있는 최상위 존재이기 때문이다.
반대로 같은 이유에서 Object.prototype에는 어떤 데이터에서도 활용할 수 있는 범용적인 메서드들만 있다.
toString hasOwnProperty valueOf isPrototypeOf 등은 모든 변수가 마치 자신의 메서드인 것처럼 호출할 수 있다.
앞서 '프로토타입 체인상 가장 마지막에는 언제나 Object.prototype이 있다'고 했는데,
예외적으로 Object.create을 이용하면 Object.prototype의 메서드에 접근할 수 없는 경우가 있다.
Object.create(null)은 __proto__가 없는 객체를 생성한다.
var _proto = Object.create(null);
_proto.getValue = function(key) {
return this[key];
};
var obj = Object.create(_proto);
obj.a = 1;
console.log(obj.getValue('a')); // 1
console.dir(obj);
_proto에는 __proto__ 프로퍼티가 없는 객체를 할당했다.
다시 obj는 앞서 만든 _proto를 __proto__로 하는 객체를 할당했다.
이제 obj를 출력해보면, __proto__에는 오직 getValue 메서드만이 존재하며, __proto__ 및 constructor 프로퍼티 등은 보이지 않는다.
이 방식으로 만든 객체는 일반적인 데이터에서 반드시 존재하던 내장 메서드 및 프로퍼티들이 제거됨으로써 기본 기능에 제약이 생긴 대신, 객체 자체의 무게가 가벼워짐으로써 성능상 이점을 가진다.
자바스크립트의 기본 내장 데이터 타입들은 모두 프로토타입 체인이 1단계(객체)이거나 2단계(나머지)로 끝나는 경우만 있었지만 사용자가 새롭게 만드는 경우에는 그 이상도 얼마든지 가능하다.
대각선의 __proto__를 연결해나가기만 하면 무한대로 체인 관계를 이어나갈 수 있다.
이 방법으로부터 다른 언어의 클래스와 비슷하게 동작하는 구조를 만들 수 있는데, 이에 대해서는 7장에 계속...
대각선의 __proto__를 연결하는 방법은 __proto__가 가리키는 대상,
즉 생성자 함수의 prototype이 연결하고자 하는 상위 생성자 함수의 인스턴스를 바라보게끔 해주면 된다.
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는 Grade의 인스턴스를 바라본다.
Grade의 인스턴스는 여러 개의 인자를 받아 각각 순서대로 인덱싱해서 저장하고 length 프로퍼티가 존재하는 등으로 배열의 형태를 지니지만, 배열의 메서드를 사용할 수는 없는 유사배열객체이다.
일전에 유사배열객체에 배열 메서드를 적용하는 방법으로 call/apply를 소개했지만,
이번에는 기왕 생성자 함수를 직접 만든 김에 인스턴스에서 배열 메서드를 직접 쓸 수 없게끔 하고자 한다.
g.__proto__, 즉 Grade.prototype이 배열의 인스턴스를 바라보게 하면 된다.
Grage.prototype = [];
이 명령에 의해 서로 별개로 분리되어 있던 데이터가 연결되어 그림과 같이 하나의 프로토타입 체인 형태를 띠게 된다.
이제는 Grade의 인스턴스인 g에서 직접 배열의 메서드를 사용할 수 있다.
console.log(g); // Grade(2) [100, 80]
g.pop();
console.log(g); // Grade(1) [100]
g.pop(90);
console.log(g); // Grade(2) [100, 90]
g 인스턴스의 입장에서는 프로토타입 체인에 따라 g 객체 자신이 지니는 멤버, Grade의 prototype에 있는 멤버, Array.prototype에 있는 멤버, 끝으로 Object.prototype에 있는 멤버에 까지 접근할 수 있게 됐다.
두 단계 이상의 체인을 지니는 다중 프로토타입 체인도 가능하다는 사실을 확인한 정도로 만족하고 이유, 구현 방식 및 문제 해결은 다음 장에서 계속...
new 연산자와 함께 호출하면 Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스가 생성되는데, 이 인스턴스에는 __proto__라는, Constructor의 prototype 프로퍼티를 참조하는 프로퍼티가 자동으로 부여된다.__proto__는 생략 가능한 속성이라서, 인스턴스는 Constructor.prototype의 메서드를 마치 자신의 메서드인 것처럼 호출할 수 있다.Constructor.prototype에는 constructor라는 프로퍼티가 있는데, 이는 다시 생성자 함수 자신을 가리킨다.__proto__ 방향을 계속 찾아가면 최종적으로는 Object.prototype에도달한다.__proto__안에 다시 __proto__를 찾아가는 과정을 프로토타입 체이닝이라고 하며,Object.prototype에는 모든 데이터 타입에서 사용할 수 있는 범용적인 메서드만이 존재하며,Object 생성자 함수에 스태틱하게 담겨있다.