javascript.info, https://ko.javascript.info/function-prototype
참고 사이트에 내용을 개인적으로 복습하기 편하도록 재구성한 글입니다.
자세한 설명은 참고 사이트를 살펴보시기 바랍니다.
생성자 "함수"를 사용해 객체를 만든 경우에 프로토타입이 어떻게 동작하는지에 대해 알아보겠습니다. 생성자 함수로 객체를 만들었을 때 리터럴 방식과 다른점은 💥 생성자 함수의 프로토타입이 객체인 경우에 new
연산자를 사용해 만든 객체는 생성자 함수의 프로토타입 정보를 사용해 [[Prototype]]
을 설정한다는 것입니다.
🔥 주의
자바스크립트가 만들어졌을 당시엔 프로토타입 기반 상속이 자바스크립트의 주요 기능 중 하나였습니다.
그런데 과거엔 프로토타입에 직접 접근할 수 있는 방법이 없었습니다. 그나마 믿고 사용할 수 있었던 방법은 생성자 함수의 "prototype"
프로퍼티를 이용하는 방법뿐이었죠. 많은 스크립트가 아직 이 방법을 사용하는 이유가 여기에 있습니다.
생성자 함수(F
)의 프로토타입을 의미하는 F.prototype
에서 "prototype"
은 F
에 정의된 일반 프로퍼티라는 점에 주의해 글을 읽어 주시기 바랍니다. F.prototype
에서 "prototype"
은 객체의 숨김 프로퍼티인 [[Prototype]]
과 이름만 같을 뿐 실제론 다른 우리가 익히 알고있는 일반적인 프로퍼티입니다.
let animal = {
eats: true
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
let rabbit = new Rabbit("흰 토끼"); // rabbit.__proto__ == animal
alert( rabbit.eats ); // true
Rabbit.prototype = animal
은 new Rabbit
을 호출해 만든 새로운 객체의 [[Prototype]]
을 animal
로 설정하라."는 것을 의미합니다.
그림으로 나타내면 다음과 같습니다.
여기서 가로 화살표는 일반 프로퍼티인 "prototype"
을, 세로 화살표는 [[Prototype]]
을 나타냅니다. 세로 화살표는 rabbit
이 animal
을 상속받았다는 것을 의미합니다.
🔥 F.prototype
은 new F
를 호출할 때만 사용됩니다.
F.prototype
프로퍼티는 new F
를 호출할 때만 사용됩니다. F
를 호출할 때 만들어지는 새로운 객체의 [[Prototype]]
을 할당해 주죠.
새로운 객체가 만들어진 후에 F.prototype
프로퍼티가 바뀌면(F.prototype = <another object>
) new F
를 호출해 만드는 또 다른 새로운 객체는 를 [[Prototype]]
으로 갖게 됩니다. 다만, 기존에 있던 객체의 [[Prototype]]
은 그대로 유지됩니다.
개발자가 특별히 할당하지 않더라도 모든 함수는 기본적으로 "prototype"
프로퍼티를 갖습니다.
디폴트 프로퍼티 "prototype"
은 constructor
프로퍼티 하나만 있는 객체를 가리키는데, 여기서 constructor
프로퍼티는 함수 자신을 가리킵니다.
이 관계를 코드와 그림으로 나타내면 다음과 같습니다.
function Rabbit() {}
/* 디폴트 prototype
Rabbit.prototype = { constructor: Rabbit };
*/
예시를 실행해 직접 확인해봅시다.
function Rabbit() {}
// 함수를 만들기만 해도 디폴트 프로퍼티인 prototype이 설정됩니다.
// Rabbit.prototype = { constructor: Rabbit }
alert( Rabbit.prototype.constructor == Rabbit ); // true
특별한 조작을 가하지 않았다면 new Rabbit
을 실행해 만든 토끼 객체 모두에서 constructor
프로퍼티를 사용할 수 있는데, 이때 [[Prototype]]
을 거칩니다.
function Rabbit() {}
// 디폴트 prototype:
// Rabbit.prototype = { constructor: Rabbit }
let rabbit = new Rabbit(); // {constructor: Rabbit}을 상속받음
alert(rabbit.constructor == Rabbit); // true ([[Prototype]]을 거쳐 접근함)
constructor
프로퍼티는 기존에 있던 객체의 constructor
를 사용해 새로운 객체를 만들때 사용할 수 있습니다.
function Rabbit(name) {
this.name = name;
alert(name);
}
let rabbit = new Rabbit("흰 토끼");
let rabbit2 = new rabbit.constructor("검정 토끼");
이 방법은 객체가 있는데 이 객체를 만들 때 어떤 생성자가 사용되었는지 알 수 없는 경우(객체가 서드 파티 라이브러리에서 온 경우 등) 유용하게 쓸 수 있습니다.
"constructor"
를 이야기 할 때 가장 중요한 점은 자바스크립트는 💥 알맞은 "constructor"
값을 보장하지 않는다는 점입니다.
함수엔 기본으로 "prototype"
이 설정된다라는 사실 그게 전부입니다. "constructor"
와 관련해서 벌어지는 모든 일은 전적으로 개발자에게 달려있습니다.
함수에 기본으로 설정되는 "prototype"
프로퍼티 값을 다른 객체로 바꿔 무슨일이 일어나는지 살펴봅시다. new
를 사용해 객체를 만들었지만 이 객체에 "constructor"
가 없는 것을 확인할 수 있습니다.
function Rabbit() {}
Rabbit.prototype = {
jumps: true
};
let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false
이런 상황을 방지하고 constructor
의 기본 성질을 제대로 활용하려면 "prototype"
에 뭔가를 하고 싶을 때 "prototype"
전체를 덮어쓰지 말고 디폴트 "prototype"
에 원하는 프로퍼티를 추가, 제거해야 합니다.
function Rabbit() {}
// Rabbit.prototype 전체를 덮어쓰지 말고
// 원하는 프로퍼티가 있으면 그냥 추가합니다.
Rabbit.prototype.jumps = true
// 이렇게 하면 디폴트 프로퍼티 Rabbit.prototype.constructor가 유지됩니다.
실수로 "prototype"
을 덮어썼다 하더라도 constructor
프로퍼티를 수동으로 다시 만들어주면 constructor
를 다시 사용할 수 있습니다.
Rabbit.prototype = {
jumps: true,
constructor: Rabbit
};
// 수동으로 constructor를 추가해 주었기 때문에 우리가 알고 있던 constructor의 특징을 그대로 사용할 수 있습니다.