코어 자바스크립트 책을 읽고 배운 내용을 바탕으로 작성되었다.
자바스크립트는 프로트타입(Prototype
)기반 언어이다.
const instance = new Constructor();
Constructor
)를 new
연산자와 함께 호출하면 Constructor
에 정의된 내용을 바탕으로 새로운 인스턴스(instance
)가 생성된다.instance
에는 __proto__
라는 프로퍼티가 자동으로 부여되는데, 이 프로퍼티는 Constructor
의 prototype
이라는 프로퍼티를 참조한다.prototype
는 객체이고 이를 참조하는 __proto__
역시 객체이다.prototype
객체 내부에는 instance
가 사용할 메서드를 저장한다.instance
에서도 __proto__
프로퍼티를 통해 이 메서드에 접근할 수 있다.const Person = function(name){
this.name = name;
}
Person.prototype.getName = function(){
return this.name;
}
const suzi = new Person("Suzi");
suzi.__proto__.getName(); // undefined
Person.prototype === suzi.__proto__ // true
Person
의 instance
는 __proto__
프로퍼티를 통해 getName
메서드를 호출할 수 있다.this
에 바인딩된 대상이 잘못 지정되었다.suzi.__proto__.getName()
에서 getName
메서드 내부에서의 this
는 suzi.__proto__
이다. 이때 suzi.__proto__
에는 name
이라는 프로퍼티가 없으므로 undefined
를 반환한다.this
가 인스턴스를 바라보도록 하기 위해서는 suzi.getName()
으로 메서드를 호출해야 한다. __proto__
프로퍼티는 생략 가능한 프로퍼티로 설계되었기 때문에 __proto__
프로퍼티를 생략하고 suzi.getName()
으로 메서드를 호출하는 것이 가능하고 this
가 instance
(suzi
)를 바라보도록 할 수 있다.요약
💡 자바스크립트는 함수에 자동으로 객체인
prototype
프로퍼티를 부여한다. 이 함수를new
연산자와 함께 생성자 함수(Constructor
)로 호출하여 생성된 인스턴스(instance
)에 자동으로__proto__
프로퍼티를 부여한다.__proto__
프로퍼티는 생성자 함수의prototype
을 참조한다.
💡__proto__
프로퍼티는 생략 가능한 프로퍼티이기 때문에, 생성자 함수의prototype
에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있다.
const arr = [1, 2];
Array
를 new
연산자와 함께 호출해서 인스턴스를 생성하든, 배열 리터럴을 생성하든, instance
([1, 2])가 생성된다.__proto___
프로퍼티는 Array
의 prototype
프로퍼티를 참조하는데 __proto__
프로퍼티는 생략 가능하도록 설계되어 있기 때문에 인스턴스가 Array
의 prototype
프로퍼티 내부의 메서드를 마치 자신의 것처럼 호출할 수 있다.Array
의 prototype
프로퍼티 내부에 존재하지 않는 메서드들은 인스턴스가 직접 호출할 수 없고, Array
생성자 함수를 통해 직접 접근해야 실행 가능하다.prototype
객체 내부에는 constructor
이라는 프로퍼티가 있다.prototype
객체를 참조하는 인스턴스의 __proto__
객체 내부에도 마찬가지이다.constructor
프로퍼티는 단어 그대로 원래의 생성자 함수을 참조한다.const arr = [1, 2];
Array.prototype.constructor === Array; // true
arr.__proto__.constructor === Array; // true
arr.constructor === Array; // true
const arr2 = new arr.constructor(3, 4);
console.log(arr2); // [3, 4]
constructor
프로퍼티는 읽기 전용 속성이 부여된 예외적인 경우를 제외하고는 값을 바꿀 수 있다.const NewConstructor = function(){
console.log("this is new contructor!");
};
const dataTypes = [
1,
"test",
true,
{},
[],
function () {},
/test/,
new Number(),
new String(),
new Boolean,
new Object(),
new Array(),
new Function(),
new RegExp(),
new Date(),
new Error()
];
dataTypes.forEach(function (d){
d.constructor = NewConstructor;
console.log(d.constructor.name, '&', d instance of NewConstructor);
}
d instance of NewConstructor
에 대해 false
를 반환한다.constructor
프로퍼티를 변경하더라도 참조하는 대상이 변경될 뿐 이미 만들어진 인스턴스의 원형이 바뀐다거나 데이터 타입이 변하지 않는다.constructor
프로퍼티에 의존하는 것은 항상 안전하지 않는다.const Person = function (name) {
this.name = name;
}
const p1 = new Person("사람1"); // {name: "사람1"} true
const p1Proto = Object.getPrototypeOf(p1);
const p2 = new Person.prototype.constructor("사람2"); // {name: "사람2"} true
const p3 = new p1Proto.constructor("사람3"); // {name: "사람3"} true
const p4 = new p1.__proto__.constructor("사람4"); // {name: "사람4"} true
const p5 = new p1.constructor("사람5"); // {name: "사람5"} true
[p1, p2, p3, p4, p5].forEach(function (p){
console.log(p, p instanceof Person);
})
생성자 함수
[Constructor]
[instance].__proto__.constructor
[instance].constructor
Object.getPrototypeOf([instance]).constructor
[Constructor].prototype.constructor
prototype 객체
[Constructor].prototype
[instance].__proto__
[instance]
Object.getPrototypeOf([instance])
__proto__
프로퍼티를 검색하는 순서로 진행된다.const Person = function (name){
this.name = name;
}
Person.prototype.getName = function(){
return this.name;
}
const iu = new Person("지금");
iu.getName = function(){
return `바로 ${this.name}`;
};
console.log(iu.getName()); // 바로 지금
console.log(iu.__proto__.getName()); // undefined
Person.prototype.name = "이지금";
console.log(iu.__proto__.getName()); // 이지금
console.log(iu.__proto__.getName().call(iu)); // 지금
__proto__
의 메서드도 우회적인 방법을 통해서 접근할 수 있다.prototype
프로퍼티는 객체이므로 prototype
프로퍼티 내부의 __proto__
프로퍼티가 Object.prototype
을 참조한다.__proto__
는 Object.prototype
과 연결된다.__proto__
는 생략 가능한 프로퍼티이기 때문에 배열이 Array.prototype
내부의 메서드를 마치 자신의 것처럼 실행할 수 있고, 마찬가지로 Object.prototype
내부의 메서드도 자신의 것처럼 실행할 수 있다.💡 어떤 instance
의 __proto__
프로퍼티 내부에 다시 __proto__
프로퍼티가 연쇄적으로 이어지는 것을 Prototype chain
이라고 하고, 이 체인을 통해 검색하는 것을 Prototype chaining
이라고 한다.
instance
에서 어떤 메서드를 호출하면 자바스크립트 엔진은 자신의 프로퍼티를 검색해서 원하는 메서드가 있으면 그 메서드를 호출하고, 없다면 __proto__
를 검색해서 있으면 그 메서드를 실행하고, 없다면 다시 __proto__
내부의 __proto__
를 검색해서 실행하는 식으로 검색한다.const arr = [1, 2];
Array.prototype.toString.call(arr); // 1, 2
Object.prototype.toString.call(arr); // [object Object]
arr.toString(); // 1, 2
arr.toString = function() {
return this.join("_");
};
arr.toString(); // 1_2
Object.prototype
내부에 정의한다면 다른 데이터 타입도 Prototype Chaining
을 통해 해당 메서드를 사용할 수 있기 때문이다.Object.prototype
아닌 Object
에 직접 부여해야 한다.Object.prototype
이 다른 참조형 데이터뿐만 아니라 기본형 데이터조차 __proto__
에 반복 접근함으로써 도달할 수 있는 최상위 존재이기 때문이다.Object.prototype
에는 어떤 데이터 타입에서도 활용할 수 있는 범용적인 메서드들만 있다.const Grade = function () {
const args = Array.prototype.slice.call(arguments);
for (let i = 0; i < args.length; i++){
this[i] = args[i];
}
this.length = args.length;
};
const g = new Grade(100, 80);
g
)에서 배열 메서드를 직접 쓸 수 있게 하기 위해서 g.__proto___
, 즉 Grade.prototype
이 배열의 인스턴스를 바라보게 하면 된다.Grade.prototype = [];
new
연산자와 함께 호출하면 Constructor
에서 정의된 내용을 바탕으로 새로운 instance
가 생성되는데 이 인스턴스에는 __proto__
프로퍼티가 자동으로 부여된다. __proto__
프로퍼티는 생성자 함수의 prototype
프로퍼티를 참조하여 __proto__
프로퍼티를 통해 인스턴스는 prototype
내부의 프로퍼티나 메서드에 접근할 수 있다. __proto__
프로퍼티는 생략 가능한 프로퍼티이기 때문에 인스턴스는 Constructor.prototype
의 메서드나 프로퍼티를 마치 자신의 것처럼 호출하거나 접근할 수 있다.Constructor.prototype
에는 constructor
라는 프로퍼티가 있는데, 이는 생성자 함수 자신을 가리킨다. constructor
프로퍼티는 인스턴스가 자신의 생성자 함수가 무엇인지 알고자 할 때 필요하다. 이 프로퍼티는 읽기 전용 속성이 부여되어 있지 않다면 값을 바꿀 수 있다. 이때 참조하는 대상만 바뀌는 것이지 이미 생성된 인스턴스의 원형이 바뀌거나 데이터 타입이 바뀌지 않는다.__proto__
프로퍼티 내부에 다시 __proto__
프로퍼티가 연쇄적으로 이어지는 것을 Prototype chain
이라고 하고, 이 체인을 통해 검색하는 것을 Prototype chaining
이라고 한다.prototype
프로퍼티는 객체이므로 prototype
프로퍼티 내부의 __proto__
프로퍼티는 Object.prototype
을 참조한다. 따라서 모든 객체의 __proto__
프로퍼티는 Object.prototype
과 연결된다.__proto__
를 검색해서 있으면 그 메서드를 실행하고, 없다면 다시 __proto__ 내부의 __proto__
를 검색해서 실행하는 식으로 검색한다.Object.prototype
에는 모든 데이터 타입에서 사용할 수 있는 범용적인 메서드만이 존재하며 객체 전용 메서드는 Object
생성자 함수에 스태틱하게 담겨 있다.