자바스크립트 프로토타입 상속과 관련된 공부 중 찾은 영어문서를 해석해 보기로했다. 현재는...개똥해석, 몇번 수정을 할 것이다.
내가 이해가 안되서 제대로 쓸 수가없다.
< 원본링크: Javascript inheritance behind the scene __proto__, [[prototype]] and prototype >
근대 닭이먼저냐 달걀이먼저냥? 둘은 그런 관계냥?
prototype은 생성자함수(constructor function)에있는 '프로퍼티'이며, 생성자함수를 통해 생성된 객체에서 무엇이 __proto__ 프로퍼티가 될지 정한다.
자바스크립트에서 상속을 이해하기위한 핵심은 자바스크립트에서 어떻게 js계란(객체를 말하는 듯)들이 엄마닭으로부터 놓여지는지 과정을 이해하는 것이다.
이 글은 프로토타입, __proto__, 자바스크립트 상속에 대한 혼동을 해결하려하는 것이다.
모든 객체는 다른 객체를 '프로토타입'으로써 가질 수 있다.
그럼 그 객체(전자)는 프로토타입-객체의 모든 프로퍼티를 상속 받는다.
객체는 자신의 프로토타입을 내부프로퍼티(internal property)인 [[Prototype]]을 통해 특정한다.
[[Prototype]] 프로퍼티에 의해 연결된 객체들의 체인을 프로토타입 체인이라고 부른다.
프로토타입 기반(또는 prototypal) 상속이 어떻게 작동하는지 보기위해, 예제를 보도록 하자 (with invented syntax for specifying the [[Prototype]] property) :
var proto = {
describe: function () {
return 'name: '+this.name;
}
};
var obj = {
[[Prototype]]: proto,
name: 'obj'
};
// 실행
> obj.describe
[Function]
> obj.describe()
'name: obj'
The __proto__ is an accessor property of the Object.prototype object. It exposes the internal prototype linkage ( [[Prototype]]) of an object through which it is accessed (by javascripttutorial).
__proto__는 Object.prototype 객체의 '접근자 프로퍼티'이다.
function Foo(name) {
this.name = name;
}
var b = new Foo('b');
var a = new Foo('a');
b.say = function() {
console.log('Hi from ' + this.whoAmI());
}
console.log(a.__proto__ === Foo.prototype); // true
console.log(a.__proto__ === b.__proto__); // true
자바스크립트 엔진은 say() 메소드를 b객체에 추가한다. Foo.prototype object에 추가하지 않고.
다이어그램을 보면, a.__proto__는 Foo.prototype 객체를 가르키는 [[Prototype]]을 노출시킨다. 유사하게, b.__proto__ 또한 a.__proto__ 와 같은 객체를 가리킨다.
constructor을 통해 객체를 생성하는 예제를 통해 어떻게 prototype 과 __proto__이 상속에서 작동하는지 볼 것입니다.
특정 패턴에 의한 객체 생성 말고도, 생성자 함수는 다른 유용한 것을 한다. 생성자함수는 새롭게 생성된 객체에 자동으로 프로토타입 객체를 포함시킨다. 이 프로토타입 객체는 생성자함수.prototype 프로퍼티 안에 저장된다.
앞에서 사용한 예제를 a생성자함수를 사용하는 b객체로 바꿔써보자. 그럼, 객체 a (a prototype) Foo.prototype은 아래처럼 작동한다.
// Foo 객체를 prototype x, calculate()와 함께 생성:
function Foo(y) {
this.y = y;
}
Foo.prototype.x = 10;
Foo.prototype.calculate = function (z) {
return this.x + this.y + z;
};
//object Foo를 사용하여 instance b를 생성:
var b = new Foo(20);
b.calculate(30); // 60
console.log(
b.__proto__ === Foo.prototype, // true
b.__proto__.calculate === Foo.prototype.calculate // true
b.__proto__.calculate === b.calculate, // true
Foo === b.constructor, // true
Foo === Foo.prototype.constructor, // true
);
위에서 보여지는 것 처럼, b는 Foo()로부터 메소드를 상속 받았다. "Foo.prototype"은 자동으로 특별한 프로퍼티인 "constructor"을 생성한다, 이것은 생성자 함수 자체(constructor function itself)로 가는 레퍼런스이다.
인스턴스(?) b는 위임을 통해 이를 발견할 수 있을 것이고, 그들의 생성자를 확인하기 위해 사용할 것이다.
이번 예시는 생성자로 객체를 만드는 것에 관한 것이다, 그러나 프로토타입 체인의 문제들에 집중할 것이다.
프로토타입 객체들은 단순히 객체이다. 그리고 그들만의 포로토타입을 가질 수 있다.
만약 프로토타입이 자신의 프로토타입에게 null이 아닌 레퍼런스를 갖고 있고 또 갖고 있고 또 (...반복...) 라면, 그걸 프로토타입체인이라고 부른다.
다음 차트는 가상의(pseudo) 자바스크립트 상속이다. 생성자 Foo는 상상의 클라스 이름이다. 그리고 foo는 그냥 하나의 instance이다.
( 해당문서에서 클라스, instance라고 하는데 맞는건지.. )
그리고 다이어그램에서 우리는 동물(Animal)로부터 개(Dog)를 상속받을 때 왜 아래처럼 하는지 볼 수 있다.
function Dog() {} // the usual constructor function
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
Foo.prototype 에 있는 prototype이 프로토타입 체인을 이루는 것이 아니다. Foo.prototype은 프로토타입 체인의 어느곳을 가르키고 있지만, Foo의 프로토타입 프로퍼티가 프로토타입 체인을 형성하지는 않는다. 프로토타입 체인을 이루는 것들은 __proto__들과 __proto__에게 포인팅 되는 객체들( 예를들면 foo.__proto__, 으로부터foo.__proto__.__proto__ 으로 올라가고 올라가고...
null에 닿을 때 까지...) 이다.
JavaScript’s Pseudo Classical 상속은 다음과 같은 방법으로 작동한다:
나는 constructor이다. 그리고 나는 그냥 함수다, 나는 prototype reference를 갖고 있다. 그리고 언제라도 foo = new Foo()가 불러지면 foo.__proto__ 가 나의 prototype object를 포인팅 하게 할 것이다.
이처럼, Foo.prototype 과 obj.__proto__ 이 둘은 서로 다른 컨셉이다.
Foo의 객체가 생성될 때, Foo.prototype은 새로운 객체의 프로토타입 체인이 가르켜야할 곳을 가르킨다. 즉, 'foo.__proto__'는 Foo.prototype이 가르키는 곳을 가르켜야 한다는 것이다.
만약 객체 woofie가 move 메소드를 갖고 있지 않다면, 프로토타입 체인을 타고 올라갈 것이다, 다른 모든 프로토타입식 상속이 그렇게 흘러가듯.
먼저 woofie.__proto__ 에게 포인팅 되고있는 객체(Dog.prototype 이 참조하는 것과 같음)로. 만약 move 메소드가 그것의 프로퍼티도 아니라면, 프로토타입을 타고 한 level더 올라간다. (이는 woofie.__proto__.__proto__ 이나 Animal.prototype 둘다같음)
Animal.prototype.move = function() { ... };
foo.constructor === Foo 이긴 하지만, constructor property는 foo만의 프로퍼티는 아니다. 사실 foo.__proto__이 가르키는 곳으로 올라가는 프로토타입 체인에서 얻어진 것이다.
Function.constructor도 같다. 위 다이어그램은 복잡해보이지고, Constructor.prototype, foo.__proto__, Foo.prototype.constructor을 보면 좀 혼란스럽긴 하다.
다이어그램을 확인하기위해, 알아두어라: foo.constructor가 값을 보여줄 것이지만, property constructor은 foo의 고유한 property는 아니며, foo.hasOwnProperty(“constructor”) 가 말하는 것 처럼, 이는 프로토타입 체인에서 얻어진 것이다.
Notes:
객체 [[Prototype]]은 인터널 프로퍼티를 통해 자신의 prototype을 지정한다.
__proto__ 은 [[Prototype]]으로의 직접적인 접근을 언어에게 준다.
prototype is the object that is used to build __proto__when you create an object with new.
프로토타입은(prototype) 당신이 new로 객체를 생성할 때 __proto__를 만들기위해 사용된 객체이다.
prototype은 생성자 함수 이외의 객체, 또는 인스턴스들에서 사용 불가하다.
prototype은 함수들에서만 사용 가능하다, 함수들이 Function과 Object에서 복사된 것들이기 때문에. 그러나 __proto__ 은 어디서나 사용가능하다.
( new Foo ).__proto__ === Foo.prototype //true
( new Foo ).prototype === undefined //true
delegate prototypes and concatenative inheritance
Cat.prototype = new Animal();
//it will properly follow -
//the prototype chain through the inheritance hierarchy.
Cat.prototype = Animal.prototype
//any runtime changes to -
//the Cat prototype would also affect the Animal
정적(static) properties/functions들은 프로토타입에 존재하지 않는다. 프로토타입은 새로운 인스턴스가 생성될 때만 사용된다.