어떤 생성자 함수를 new 연산자와 함께 호출하면 Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스가 생성된다. 이때 인스턴스에는
__proto__
라는 프로퍼티가 자동으로 부여되는데 이 프로퍼티는 Constructor의 prototype이라는 프로퍼티를 참조한다.
var Person = function (name) {
this._name = name;
};
Person.prototype.getName = function () {
return this._name;
};
var suzi = new Person("Suzi");
suzi.__proto__.getName(); // undefined
suzi.getName(); // Suzi
undefined가 나왔다는 것은 이 변수가 '호출할 수 있는 함수'에 해당한다는 것을 의미한다. 만약 실행할 수 없는, 즉 함수가 아닌 다른 데이터 타입이었다면 TypeError가 발생했을 것이다.
❓ 왜 undefind가 출력되나
👉 this에 바인딩된 대상이 잘못 지정됐기 때문이다. this는 suzi가 아닌 suzi.__proto__
라는 객체이기 때문에 undefined가 반환된 것이다.
__proto__
는 생략 가능한 프로퍼티다. 따라서 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있게 된다.
var arr = [1, 2];
arr.forEach(function () {}); // 정상
Array.isArray(arr); // 정상 true
arr.isArray(); // ❌ TypeError : arr.isArray is not a function
Array를 new 연산자와 함께 호출해서 인스턴스를 생성하든, 그냥 배열 리터럴을 생성하든, 어쨋든 인스턴스인 [1, 2]가 만들어진다. 이 인스턴스의 __proto__
는 Array.prototype을 참조하는데 __proto__
가 생략 가능하도록 설계돼 있기 때문에 인스턴스가 push, pop, forEach 등의 메서드를 마치 자신의 것처럼 호출할 수 있다.
❗ 한편 Array의 prototype 프로퍼티 내부에 있지 않은 from, isArray등의 메서드들은 인스턴스가 직접 호출할 수 없다. 이들은 Array 생성자 함수에서 직접 접근해야 실행이 가능하다.
✔️ constructor 프로퍼티
생성자 함수의 프로퍼티인 prototype 객체 내부에는 constructor라는 프로퍼티가 있다. 인스턴스의
__proto__
객체 내부에도 마찬가지이다. 이 프로퍼티는 단어 그대로 원래의 생성자 함수를 참조한다.
var arr = [1, 2];
Array.prototype.constructor === Array; // true
arr.__proto__.constructor === Array; // true
arr.constructor === Array; // true
모두 동일한 대상을 가리킨다.
[Constructor][instance].__proto__.constructor[instance].constructor;
Object.getPrototypeOf([instance]).constructor[Constructor].prototype
.constructor;
모두 동일한 객체(prototype)에 접근할 수 있다.
[Constructor].prototype[instance].__proto__[instance];
Object.getPrototypeOf([instance]);
✔️ 프로토타입 체인
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__
에는 Object.prototype이 연결된다. 어떤 데이터의__proto__
프로퍼티 내부에 다시__proto__
프로퍼티가 연쇄적으로 이어진 것을 프로토타입 체인이라 하고, 이 체인을 따락며 검색하는 것을 프로토타입 체이닝이라고 한다. 프로토타입 체인상 가장 마지막에는 언제나 Object.prototype이 있다.
❗ 하지만 예외적으로 Object.create를 이용하면 Object.prototype의 메서드에 접근할 수 없는 경우가 있다. Object.create(null)은 __proto__
가 없는 객체를 생성한다.