기존 클래스 기반 언어에서는 상속을 사용하나, js는 어떤 객체를 프로토타입으로 삼고 이를 참조(복제)함으로써 상속과 비슷한 효과를 낸다.
var instance = new Constructor();
new 연산자로 Constructor를 호출하면 instance가 만들어지는데, 이 instance에는 생략 가능한 프로퍼티인 proto가 있고, 이게 Constructor의 prototype을 참조한다.
⇒ instance.proto 는 Constructor.prototype을 참조한다.
prototype은 객체로 이를 참조하는 .proto([[prototype]]) 역시 객체다.
prototype 객체 내부에 인스턴스가 사용할 메서드를 저장하고 인스턴스에선 이 메서드를 .proto 를 통해 접근할 수 있게 된다.
자바스크립트는 함수에 자동으로 객체인 prototype 프로퍼티를 생성해 놓는데, 해당 함수를 생성자 함수로 사용할 경우, 즉 new 연산자와 함께 함수로 호출할 경우, 그로부터 생성된 인스턴스에는 숨겨진 프로퍼티인 .proto 가 자동으로 생성되며, 이 프로퍼티는 생성자 함수의 prototype 프로퍼티를 참조한다.
이 .proto 프로퍼티는 생략 가능하도록 구현돼 있으므로 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자기 것처럼 해당 메서드나 프로퍼티에 접근할 수 있게 된다.
ES5 에선 instance.proto 로 직접 접근 대신 getPrototypeOf(instacne)/ Reflect.getPrototype(instance)를 통해 접근할 수 있도록 정의함
하지만 실제 브라우저에서는 전자와 같은 직접 접근을 허용했고 결국 ES6 에서는 브라우저 호환성 측면에서 이를 허용함
(질문이 나올까 싶긴 한데 저는 몰랐던 내용이라 넣어봤습니다)
var arr = [1, 2];
console.dir(arr);
console.dir(Array);
위와 같이 Array를 살펴보면 constructor 프로퍼티에 자기 자신을 참조하고 있음을 확인할 수 있다.
왜 자기 자신을 참조할까? 인스턴스로부터 그 원형을 알기 위해서다.
Array.prototype.constructor === Array; // true
arr.__proto__.constructor === Array; // true
arr.constructor === Array; // true
인스턴스의 .proto 가 생성자 함수의 prototype 프로퍼티를 참조하고, .proto 는 생략이 가능하기 때문에 인스턴스에서 직접 Constructor에 접근할 수 있다.
즉, 아래와 같이 작성해도 오류 없이 동작한다.
var arr2 = new arr.constructor(3, 4);
console.log(arr2); // [3,4]
다만, 읽기 전용 속성 외의 경우를 제외하곤 constructor의 값을 바꿀 수도 있기 때문에 어떤 인스턴스의 생성자 정보를 알아내기 위해 constructor 프로퍼티에 의존하는 게 항상 안전하진 않다.
자바스크립트 엔진이 메서드를 찾을 때 가장 가까운 대상인 자기 자신의 프로퍼티를 검색하고, 없으면 그 다음으로 가까운 대상인(바로 위) [[Prototype]]을 검색하는 순서로 진행된다. 이름이 같은 메서드가 있다면 메서드 오버라이드가 발생한다.
console.dir(arr);
배열을 살펴보면, [[Prototype]] 안에 [[Prototype]]이 또 등장하는데 prototype 객체가 객체이기 때문이다. 기본적으로 모든 객체의 .proto 에는 Object.prototype과 연결된다.
모든 생성자 함수는 모두 함수이기 때문에 Function 생성자 함수의 prototype과도 연결된다.
Function 생성자 함수 역시 함수이기 때문에 다시 Function 생성자 함수의 prototype과 연결되어 결국 .proto 의 constructor의 .proto 의 constructor ... 를 재귀적으로 반복한다.
결국 같은 Function 생성자 함수를 가리킨다.
어떤 생성자 함수든 prototype은 반드시 객체이므로 Object.prototype은 언제나 프로토타입 최상단에 존재한다. 따라서 객체에서만 사용할 메서드는 다른 데이터 타입처럼 프로토타입 객체 안에 정의할 수 없다. 이 안에 정의할 경우 다른 데이터 타입도 해당 메서드를 사용할 수 있게 되기 때문이다.
그러므로 객체 전용 메서드들은 Object.prototype이 아닌 Object에 static method로 부여되어 있다.
또한, this 사용이 아닌 대상 인스턴스를 인자로 직접 주입하는 방식으로 구현돼 있다. 이는 생성자 함수인 Objectdhk 인스턴스인 객체 리터럴 사이에는 this를 통한 연결이 불가능하기 때문이다.