프로토타입 상속과 프로토타입 체인
- 생성자 함수(클래스)의 프로토타입 속성은, 그로부터 생성된 인스턴스의 프로토타입이 된다.
- 인스턴스의 constructor 속성은 해당 클래스를 가리키고, 클래스의 prototype 속성은 인스턴스를 가리키고 있다.
- 한 인스턴스의 (.__proto__로 확인할 수 있는) prototype 즉 클래스의 prototype 속성도 "객체"이다. 자바스크립트에서 모든 객체는 프로토타입을 가지고 있다.
- 예시를 들자면, hyejung.calcAge()라는 메서드를 사용할 때 자바스크립트는 먼저 인스턴스 객체인 hyejung 객체 안에 calcAge 프로퍼티가 존재하는지 조회한다. 없을 경우 그 상위 프로토타입 객체를 조회하고, 거기서도 없을 경우 그 프로토타입의 프로토타입을 참조하게 된다. 즉, calcAge 라는 메서드를 hyejung 객체의 상위 객체, prototype에게 위임하여 실행한다.
- 모든 프로토타입 객체의 최상위 프로퍼티는 Object.prototype 이 된다. 그리고 이렇게 검색해서 찾는 일련의 과정을 "프로토타입 체인" 이라고 한다.
// Person 생성자
function Person() {
this.birthYear = 1997;
Person.prototype.calcAge = function () {
return 2022 - this.birthYear;
};
}
// 인스턴스 객체 hyejung
const hyejung = new Person();
// hyejung 인스턴스 객체에서 calcAge 메서드를 실행 => 해당 객체에서 먼저 조회한 뒤 상위 프로토타입을 조회.
console.log(hyejung.calcAge());
// 해당 인스턴스 객체에는 존재하지 않지만, prototype 에 위임하여 실행할 수 있는 것이다.
// 참고로 hasOwnProperty 라는 메서드도 프로토타입의 상위 객체 즉, Object.prototype 속성에 위임하여 실행됨.
console.log(hyejung.hasOwnProperty('calcAge')); // false
빌트인 메서드와 프로토타입 상속
- 각종 빌트인 메서드 또한 생성자로 부터 생성된 각 프로토타입 속성, 예를들면 Array.prototype에 그 메서드들이 등록되어있기 때문에, 모든 배열에서 프로토타입 체인으로 그 메서드를 실행시킬 수 있는 것이다. (그래서 mdn에 메서드를 검색하면 Array.prototype.filter 이런 이름으로 검색되는 것.)
- 모든 객체가 객체 생성자의 prototype 속성으로부터 메서드를 상속받는다. 그렇다면, 그 최상위 프로토타입에 메서드를 추가한다면? 같은 유형의 객체는 실제로 그 메서드를 가지고 있지 않아도, 새로 생성된 메서드에 접근할 수 있을 것이다.
- 하지만 단지 그럴 수도 있다는 예시일 뿐 실제 프로젝트에서 이런 식으로 사용하면 안된다!
(다음 버전 자바스크립트에서 같은 이름의 메서드가 생길 수도 있고 (방식은 다르겠지만) 그런 경우 코드가 망가지게 될 것이다.)
const arr = [2, 3, 4, 5, 5, 66, 66, 444];
// 최상위 프로토타입에 unique라는 직접 만든 메서드를 추가
Array.prototype.unique = function () {
return [...new Set(this)];
};
// 어떤 배열에서도 unique 메서드에 접근할 수 있다.(예시일뿐 이렇게 쓰지 마세요!)
console.log(arr.unique());
// [ 2, 3, 4, 5, 66, 444 ]
DOM과 프로토타입 체인
- 참고로 DOM 객체도 객체이기 때문에 프로토타입 체인이 발생할 수 있다. createElement로 생성한 div 도 상위 클래스에 의해 생성된 인스턴스 객체이다.
// + DOM Element도 객체이기 때문에, 상위 프로토타입을 갖고 프로토타입 체인이 발생한다.
let div = document.createElement('div');
console.log(div.__proto__);
console.log(div.__proto__.__proto__);
console.log(div.__proto__.__proto__.__proto__);
console.log(div.__proto__.__proto__.__proto__.__proto__);
console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__);
console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__);
console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__);
ES6 문법과 프로토타입 상속
- ES6의 클래스 문법은 기본적으로 ES5의 생성자 함수와 동일한 방식으로 동작한다. 단지 문법이 더 보기 편해졌을 뿐임.
- 클래스 문법 내부의 constructor 함수는 생성자 함수 역할을 한다. 이 함수의 매개변수가 인스턴스를 실행할 때 전달하는 전달 인자를 받는 부분이다. this 키워드로 프로퍼티를 셋팅하는 영역이다.
- constructor의 중괄호 바깥 영역에서 메서드를 정의한다. 생성자 함수의 prototype 방식에 비해서 훨씬 가독성이 높다. 이렇게 등록된 메서드들은 인스턴스에 그대로 복사되는 것이 아니라 생성자 함수에서 prototype 으로 등록한 것과 동일하게 클래스의 prototype 속성에 등록된다.
class CounterES6 {
// class로 생성된 객체 즉 인스턴스를 생성하고 초기화하기 위한 메서드.
constructor() {
this.value = 0;
}
// 인스턴스의 메서드는 아래와 같이 정의한다.
// constructor 메서드 바깥에서 정의하는 메서드들은, 이 클래스의 프로토타입 속성이 된다.
increase() {
this.value++;
}
decrease() {
this.value--;
}
getValue() {
return this.value;
}
}
- 해당 클래스로 생성된 인스턴스 객체의 .__proto__ 속성. 즉 클래스의 prototype 속성을 콘솔로 찍으며 ㄴ아래의 결과가 나온다.
console.log(counterEs6); //인스턴스 객체
console.log(counterEs6.__proto__); // 인스턴스 객체의 프로토타입 조회
'JavaScript > JavaScript' 카테고리의 다른 글
[JavaScript] 비동기 흐름 제어 -1 : 콜백함수, promise (0) | 2022.07.28 |
---|---|
[JavaScript] 클래스간의 상속 (프로토타입 상속) (0) | 2022.07.25 |
[JavaScript] 프로토타입 (0) | 2022.07.22 |
[JavaScript] OOP in JavaScript : 생성자 함수, 클래스 문법 (0) | 2022.07.22 |
[JavaScript] 객체지향 프로그래밍 : OOP란 무엇인가? (0) | 2022.07.22 |