JavaScript에서는 객체를 상속하기 위해 프로토타입이라는 방식을 사용한다.
이는 모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체를 가진다는 의미이다.
프로토타입 객체도 또 다시 상위 프로토타입 객체로부터 메소드와 속성을 상속 받을 수 있고 그 상위 프로토타입 객체도 마찬가지이다.
이를 프로토타입 체인(prototype chain)이라 부르며 다른 객체에 정의된 메소드와 속성을 한 객체에서 사용할 수 있도록 하는 근간이다.
정확히 말하자면 상속되는 속성과 메소드들은 각 객체가 아니라 객체의 생성자의 prototpye 이라는 속성에 정의되어 있다.
JavaScript에서는 객체 인스턴스와 프로토타입 간의 연결이 구성되며 이 연결을 따라 프로토타입 체인을 타고 올라가서 속성과 메소드를 탐색한다.
생성자 함수를 하나 정의하고
function Person(first) {
this.first = first;
}
그리고 인스턴스도 하나 만들자.
var person1 = new Person('Bob')
그리고 나서 콘솔창에 "person1."을 입력하면
아래 사진처럼 해당 객체의 멤버이름을 자동완성 팝업으로 보여준다.
여기 직접 정의한 속성인 first를 확인할 수 있고, 또한 "proto"를 포함한
Person()의 프로토타입 객체인 Object에 정의된 다른 멤버들도 볼 수 있다. 이는 프로토타입 체인이 동작한다는 증거이다.
만약 실제로 Object에 정의되어 있는 메소드를 person1에서 호출하면 어떻게 될까?
peson1.valueOf()
이 메소드는 호출된 객체의 값을 단순 반환하는데
1) 브라우저는 우선 person1 객체가 valueOf() 메소드를 가지고 있는지 체크한다.
2) 없으므로 person1의 프로토타입 객체( Person() 생성자의 프로토타입 )에 valueOf() 메소드가 있는지 체크한다.
3) 여전히 없으므로 Person() 생성자의 프로토타입 객체의 프로토타입 객체( Object() 생성자의 프로토타입 )가 valueOf() 메소드를 가지고 있는지 체크한다. 여기에 있으니 호출이 끝난다.
주의! 프로토타입 체인에서 한 객체의 메소드와 속성들이 다른 객체로 복사되는 것은 아니다. 체인을 타고 올라가면서 접근을 하는 것 뿐이다, 특정 객체의 프로토타입 객체에 바로 접근하는 공식적인 방법은 없다. 하지만 많은 수의 모던 브라우저들이 (proto) 속성을 통해 특정 객체의 프로토타입 객체에 접근할 수 있도록 구현하였다.
ECMAScript2015부터는 Object.getPrototypeOf(obj) 함수를 통해 객체의 프로토타입 객체에 바로 접근할 수 있게 되었다.
그럼 상속 받은 속성과 메소드들은 어디에 정의되어 있을까?
정답은 Object.prototype.이다. prototype 속성도 하나의 객체이며 프로토타입 체인을 통해 상속하고자 하는 속성과 메소드를 담아두는 버킷으로 주로 사용되는 객체이다.
전역객체인 String, Date, Number, Array의 프로토타입에 정의도니 메소드와 속성들을 체크해보면 이 프로토타입 객체들에는 이미 수많은 메소드가 정의되어 있어서 임의의 문자열 객체를 생성했을 때 인스턴스가 생성되는 즉시 split(), indexOf(), replace()등의 문자열을 위한 유용한 메소드를 사용할 수 있다.
!!중요한 것 prototype속성은 JavaScript에서 가장 헷갈리는 명칭 중 하나이다.
프로토타입 객체는 (proto)속성으로 접근 가능한 내장객체이고, prototype 속성은 상속시키려는 멤버들이 정의된 객체이다.
새 인스턴스를 생성하기 위해서 create() 메소드를 사용하였는데
아래 코드를 보자.
var person2 = Object.create(person1);
위 코드는 주어진 객체를 프로토타입 객체로 삼아 새로운 객체를 생성하는 것이다.
person2는 person1을 프로토타입 객체로 삼는다.
person2.proto
위 처럼 입력하면 콘솔 창에는 person1이 출력된다.
모든 생성자 함수는 constructor 속성을 지닌 객체를 프로토타입 객체로 가지고 있다.
이 constructor 속성은 원본 생성자 함수 자신을 가리키고 있다.
Person.prototype 속성에 정의된 속성들은 person() 생성자로 생성된 모든 인스턴스에 사용될 수 있다. 따라서 person1과 person2에서도 constructor 속성에 접근할 수 있다.
예를들어
person1.constructor
person2.constructor
두 구문 모두 Person() 생성자 함수를 반환한다.
constructor 속성에 괄호를 붙이고 실행하여 새 인스턴스를 생성하는 트릭도 있다.
예를들어
var person3 = new person1.constructor('Karen')
생성자의 prototype 속성을 수정하는 법을 알아보자.
프로토타입에 메소드를 추가하면 해당 생성자로 생성된 모든 객체에서 사용 가능하다.
Person.prototype.farewell = function() {
alert(this.name.first + 'has left the building. Bye for now!');
};
이렇게 저장하고
person1.farewell();
을 작성하면 원하는 작업이 수행된다.
이는 매우 유용한 기능이지만 중요한 점은 prototype에 새 메소드를 추가하는 순간 동일한
생성자로 생성된 모든 객체에서 추가된 메소드를 바로 사용할 수 있다는 점이다.
실제로 프로토타입 객체는 모든 인스턴스에서 공유하기 때문에 정의하는 즉시 별도의 갱신없이
접근이 가능하다.
단, prototype에 속성을 정의하는 것은 별로 좋은 방법이 아니다.
사실 일반적인 방식으로는 속성은 생성자에서, 메소드는 프로토타입에서 정의한다.
생성자에는 속성에 대한 정의만 있으며 메소드는 별도의 블럭으로 구분할 수 있으니 코드를 읽기가 쉬워진다.
예시)
fucntion Test(a, b, c, d)_ {
//속성 정의
}Test.prototype.x = function() { ... };
Test.prototype.y = function() { ... };
-https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes