let animal = {
eats: true
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal; // 프로토타입 객체 설정
let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
alert( rabbit.eats ); // true
위와 같이 함수에서 new
키워드를 이용해 새로운 객체를 생성하면, 함수의 .prototype
에 존재하는 객체가 새롭게 생성된 객체의 프로토타입 객체가 된다.
참고로 자바스크립트에서는 함수도 객체이다. 단순히 객체 뒤에 괄호를 넣어
객체()
와 같은 형태를 띄었을 때 무언가 실행할 뿐이다.
Rabbit.prototype
에 들어있던 객체가 생성자로 생성한 객체의 __proto__
에 할당된다.
new Function()
코드를 통해 생성된 객체의[[Prototype]]
에는Function.prototype
이 참조하던 객체가 들어간다.
주의해야 할 것은
Rabbit.prototype
자체의 레퍼런스를 할당하는 것이 아니라Rabbit.prototype
이 가지고 있던 객체의 레퍼런스를 할당한다는 것이다. 다시 말하면Rabbit.prototype === object
일 때,object
의 레퍼런스를 할당하는 것이지,Rabbit.prototype
자체를 할당하는 것이 아니다.
new Rabbit()
과 같이 new Function()
이 호출될 때만 Function.prototype
을 newObject.__proto__
에 할당한다. 단, 프로토타입 객체의 레퍼런스를 할당해서 Function.prototype
이 변경되면 newObject.__proto__
도 변경된다.
단, Rabbit.prototype
에 사용되었던 animal
은 바뀌어도 상관없다. 왜냐하면 Rabbit.prototype
이 animal
을 직접적으로 참조하지 않기 때문이다.
new Rabbit()
코드로 생성된 rabbit
의 __proto__
는 Rabbit.prototype
가 가리키던 객체를 그대로 참조했기 때문에, 해당 객체의 내용을 바꾸고, rabbit.__proto__
객체의 내용을 확인해보면 이 객체가 Rabbit.prototype
가 가리키던 객체를 그대로 참조하고 있음을 다시 확인할 수 있다.
하지만, 위와 같이 Rabbit.prototype
이 참조하는 객체를 바꿔버리는 경우에는 rabbit.__proto__
에 변화가 없다. rabbit.__proto__
는 이전에 Rabbit.prototype
에 존재하던 객체를 여전히 가리키고 있을 것이다.
모든 선언된 함수는 내부적으로 prototype
프로퍼티를 갖는다. 그리고 그 prototype
내부에는 constructor
가 들어있다.
그래서 Function.prototype
을 강제로 수정하지 않았다면, new Function()
으로 생겨난 어떤 객체에서도 .constructor()
를 통해 생성자를 호출할 수 있다.
대략 위와 같은 관계가 되는데, Function
에서는 .prototype
을 통해 Function.prototype
에 접근 가능하다. 그리고 Function.prototype
에서는 constructor
를 통해 Function
에 접근 가능하다. 그리고 new Function()
으로 인해 생겨난 객체에서도 또 프로토타입을 통해 .constructor()
를 사용하여 Function
에 접근 가능하다.
서드파티 라이브러리를 사용하다가 자바스크립트 객체가 어디서 온지 모를 때, 이를 이용하면 유용하다.
단, 자바스크립트는 constructor
의 값을 보장하지 않는다. 우리가 멋대로 object.prototype
을 수정해버리면 constructor
는 날아간다.
그래서, object.prototype = {}
식으로 프로토타입의 속성을 지어주는 것보다는, object.prototype.a = 'value'
와 같은 형식으로 프로토타입을 지어줘야 constructor
도 지키면서 프로토타입의 값도 넣어줄 수 있다.
function Rabbit() {}
Rabbit.prototype = {
eats: true
};
let rabbit = new Rabbit();
Rabbit.prototype = {};
alert( rabbit.eats ); // ?
위의 결과 값은 true
이다 그 이유는 아무리 Rabbit.prototype
에 {}
라는 빈 객체를 할당해도 rabbit.__proto__
는 Rabbit.prototype
이 참조하던 객체를 그대로 참조한다. 되려 Rabbit.prototype.eats = false
와 같은 코드를 입력했다면, rabbit.__proto__
가 참조하던 객체가 직접 변경되어 결과는 false
가 되었을 것이다.
function Rabbit() {}
Rabbit.prototype = {
eats: true
};
let rabbit = new Rabbit();
Rabbit.prototype.eats = false;
alert( rabbit.eats ); // ?
이건 위에 설명했듯, 참조하는 객체의 프로퍼티를 직접 바꾼 케이스라 결과도 false
가 나온다.
function Rabbit() {}
Rabbit.prototype = {
eats: true
};
let rabbit = new Rabbit();
delete rabbit.eats;
alert( rabbit.eats ); // ?
이 코드에서 출력하는 값은 true
이다. rabbit.eats
는 현재 프로토타입 객체를 거쳐 출력은 가능하지만, 실제 rabbit.eats
에 직접적으로는 아무것도 없는 상태이기 때문이다.
function Rabbit() {}
Rabbit.prototype = {
eats: true
};
let rabbit = new Rabbit();
delete Rabbit.prototype.eats;
alert( rabbit.eats ); // ?
이 코드에서 출력하는 값은 undefined
이다. rabbit.__proto__
가 참조하던 객체의 eats
를 직접 지워버렸다.
위의 로그 내용처럼 constructor
를 new person1.constructor()
와 같은 코드로 응용할 수도 있다. new
라는 키워드를 이렇게 붙이는 것이 좀 낯설어보일 수 있다.
function Person(name) {
this.name = name;
}
Person.prototype = {};
const newPerson1 = new Person("Peter");
const newPerson2 = new newPerson1.constructor("jake");
console.log(newPerson1.name);
console.log(newPerson2.name);
위 코드는 Function.prototype.constructor
의 값은 보장되지 않는다는 특성을 이용했다. 기본적으로 Function.prototype.constructor
는 자동으로 생성되지만, 사용자에 의해 수정될 수 있다. 위의 경우에는 Function.prototype
자체가 {}
로 변해버렸다.
위의 코드를 잘 보면 newPerson1.name
을 로그로 찍어봤을 때는 Peter
가 잘 나오는데, newPerson2.name
을 로그로 찍어보면 jake
가 나오지 않는다. 그 이유는 newPerson2
를 생성할 때 newPerson1.constructor
로 생성을 하였는데, 우리가 Person.prototype
를 수정하지 않았다면 정상적으로 생성됐지만, 우리는 Person.prototype
을 {}
값으로 바꾸어놓았다. 그래서 자바스크립트는 newPerson1.__proto__
에서 constructor
프로퍼티를 찾아보지만 찾아낼 수 없다. 그래서 자바스크립트는 newPerson1.__proto__.__proto__
와 같이 프로토타입 체인을 타고 constructor
를 찾으려 한다. newPerson1.__proto__
는 {}
이므로, {}
는 일반 자바스크립트 객체이고 프로토타입으로 Object
를 갖는다.
마침내 자바스크립트는 Object.constructor
를 찾아 해당 코드를 수행한다. Object.constructor("jake")
가 실행되는데, Object.constructor()
는 내부에 무슨 인자가 오더라도 오직 {}
만 반환한다. 그러므로 {}.name
은 undefined
가 되는 것이다.