JavaScript는 흔히 프로토타입 기반 언어(prototype-based language)라고 부른다.
여기서 프로토타입은 원형 객체를 의미한다.
클래스의 새로운 인스턴스가 만들어질 때 상속되는 속성과 메소드들은 각 인스턴스에 새롭게 만들어져 저장되는 것이 아닌 클래스의 prototype
속성 객체에 저장된다.
class Human {
constructor(name, age) {
this.name = name;
this.age = age;
}
sleep() {
console.log(`${this.name} is sleeping now...`);
}
}
먼저 Human
클래스를 만들었다. 이 클래스를 개발자 콘솔에서 console.dir()
로 확인해보자.
Human
클래스의 멤버 변수인 length
와 name
은 확인 가능하지만 sleep
메서드는 찾아볼 수 없다.
🎯 여기서 잠깐!
멤버 변수(혹은 데이터 멤버 / 클래스 필드)란?
클래스 내부의 캡슐화된 변수를 말한다. 쉽게 말해, 자바스크립트의 생성자 함수에서this
에 추가한 속성(property)를 클래스 기반 객체지향 언어에서는 멤버 변수라고 부른다.
name
멤버 변수 바로 다음의 prototype
객체가 보이는가? 저것이 바로 프로토타입 객체이다.
조금 더 자세히 살펴보자.
prototype
객체 안에서 생성자 함수 constructor
와 sleep
메서드가 존재함을 확인 할 수 있다.
인스턴스(자식 객체)는 클래스(부모 객체)의 prototype
객체를 참조하여 속성과 메서드를 사용할 수 있다.
인스턴스를 하나 만들어보자.
const student = new Human('David', '21');
해당 인스턴스를 확인해보면 다음과 같이 나타난다.
멤버 변수인 age
와 name
은 보이지만 클래스에 존재하던 sleep
메서드는 보이지가 않는다.
위에서도 말했듯이 상속되는 속성과 메서드들은 인스턴스 객체 내부가 아닌 클래스의 prototype
에 저장된다.
[[Prototype]]
를 확인해보자.
클래스의 prototype
객체에 저장돼있는 constructor
와 sleep
을 확인할 수 있다.
객체는 자신의 프로토타입을 가리키는 [[Prototype]]
인터널 슬롯을 가지며 이것은 상속을 위해 사용된다.
함수 또한 객체이므로 [[Prototype]]
인터널 슬롯을 가진다. 하지만 함수 객체는 일반 객체와 다르게 prototype
속성도 가지고있다.
Function.prototype
을 가리킨다.__proto__
속성으로 자식객체에서 부모객체의 [[Prototype]]에 접근할 수 있다.프로토타입 객체는 constructor
속성을 가진다. constructor
는 객체 입장에서 자신을 생성한 객체를 가리킨다.
function Person(name) {
this.name = name;
}
let foo = new Person('Carl');
// Person() 생성자 함수에 의해 생성된 객체(prototype)를 생성한 객체는 Person() 생성자 함수이다.
console.log(Person.prototype.constructor === Person);
// output : true
// foo 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(foo.constructor === Person);
// output : true
// Person() 생성자 함수를 생성한 객체는 Function() 생성자 함수이다.
console.log(Person.constructor === Function);
OOP의 4가지 특징 중 상속을 구현할때 프로토타입 체인을 사용한다.
자식 객체의 경우 부모 객체의 속성이나 메서드를 사용하고 싶을 때 프로토타입을 이용한다.
이 때 원하는 속성이나 메서드가 부모 클래스에서 찾을 수 없는 경우 [[Prototype]]
이 가르키는 링크를 타고 한
단계 더 올라가 검색을 시작한다. 이를 원하는 속성이나 메서드를 찾거나, [[Prototype]]
이 가르키는 링크가 없을때까지 수행한다.
이처럼 부모 - 자식 객체의 프로토타입 객체들이 연결되어있는 모습이 사슬과 같다하여 프로토타입 체인이라고 한다.
const newDiv = document.createElement('div');
newDiv.addEventListener('click', () => console.log('hi'));
newDiv
라는 변수에 div
태그를 저장하였다. 그 후 클릭 이벤트를 addEventListener
메서드를 통해
바인딩했다. 이 addEventListener
메서드는 어디에 존재할까?
[[Prototype]]
에 접근하기 위해 .__proto__
속성을 사용하였다. 이를 통해 div
태그는 HTMLDivElement
인터페이스의 인스턴스인 것을 알 수 있었다. 하지만
프로토타입 객체에 addEventListener
메서드는 보이지 않는다. 더 상위로 올라가보자.
.__proto__
속성을 통해 5번이나 링크를 타고 올라간 이후에야 EventTarget
객체의 프로토타입
객체에서 addEventListener
메서드를 발견할 수 있었다.
🎯 참고
PoeimaWeb - 프로토타입