최신 ES6 자바스크립트에는 class 문법이 추가됐지만, class 문법이 디폴트는 아니기 때문에 자바스크립트에서는 class 대신 prototype을 사용한다. 프로토타입은 말 그대로 객체의 원형이라고 볼 수 있으며, 상속의 기능을 구현할 수 있다는 특징이 있다.
function Grandfather() {}
Grandfather.prototype.haircolor = 'brown';
function Father() {}
Father.prototype = new Grandfather();
function Son() {}
Son.prototype = new Father();
Father.haircolor // 'brown'
Son.haircolor // 'brown'
Grandfather
이라는 함수를 선언하였을 때, 함수 Grandfather은 생성자(constructor) 자격을 부여받는다. 여기서 생성자란 new
키워드를 통해 다른 객체를 만들 수 있는 함수를 말한다. 또한 함수 Grandfather은 해당 함수가 생성됨과 동시에 Grandfather.prototype
이라는 객체를 가지게 된다. 이 prototype을 통해 해당 객체 내에 속성이나 메서드를 추가할 수 있다.
typeof Grandfather; // function
typeof Grandfather.prototype // object
그리고 키워드 new
를 사용하여 Grandfather로부터 Father
이라는 새로운 객체를 생성하게 되면, 객체 Father은 Father.prototype
이라는 객체를 가지게 되며, 객체 Grandfather.prototype
의 속성을 자동으로 상속받게 된다.
Father.prototype.lastname = 'Wisley'
객체 Father.prototype
에 새로운 속성을 추가해준 뒤에 똑같은 방식으로 Son
이라는 객체를 Father로부터 생성했을 때, 객체 Son은 객체 Father.prototype
의 속성과 더불어 Grandfather.prototype
의 속성 또한 자동으로 상속받게 된다. Son은 Father로부터 생성되었지만, Father은 Grandfather로부터 생성이 되었기 때문에 최상위 객체인 Grandfather의 속성까지 모두 상속받을 수 있는 것이다.
Son.prototype.lastname; // 'Wisley'
Son.prototype.haircolor; // 'Brown'
Son.prototype.haircolor === Father.prototype.haircolor // true
Son.prototype.haircolor === Grandfather.prototype.haircolor // true
👉 이렇게 prototype으로 인해 연결된 상속관계를 Prototype Chain이라고 한다.
1) 함수는 정의되는 순간,
constructor
와 __proto__
가 존재constructor
: 생성된 함수를 의미__proto__
: prototype Linknew
키워드를 통해 다른 객체 생성 가능2) new 키워드를 통해 객체가 생성되는 순간,
__proto__
속성을 가지게 된다.__proto__
을 통해 상위 함수의 Prototype Object의 값을 참조할 수 있는 것이다.Prototype Object는 속성을 마음대로 추가하거나 삭제할 수 있다. 그렇기 때문에 함수 Parent를 통해 새롭게 생성된 객체 child1과 child2는 Prototype Link를 통해 Parent.prototype의 속성 혹은 메서드를 참조할 수 있게 된다. 만약, 최상위 객체의 Prototype Object까지 찾아봤지만 값을 찾지 못하였을 경우에는 Undefined를 리턴하게 된다.
👉 이렇게
__proto__
를 통해 상위 prototype과 연결되어 있는 상속관계를 Prototype Chain이라고 한다.
function Parent() {
this.home = 'seoul';
}
const child1 = new Parent();
const child2 = new Parent();
child1.home; // 'seoul'
child2.home; // 'seoul'
예를 들어 2개의 객체(child1
, child2
)를 생성했을 때 child1과 child2는 home이라는 변수를 동일하게 가지고 있기 때문에, 객체수(2개) x 변수의 수(1개)만큼 메모리에 할당이 된다. 객체의 수가 n개까지 늘어나면 n개 * 변수의 갯수만큼 메모리에 할당이 되는 것이다.
function Parent() {}
Parent.prototype.home = 'seoul';
const child1 = new Parent();
const child2 = new Parent();
child1.home; // 'seoul'
child2.home; // 'seoul'
하지만 프로토타입을 사용하면 Parent.prototype
이라는 빈 객체(Object)가 생성이 되고, Parent
함수로부터 생성된 새로운 객체(child1
, child2
)들은 저 빈 객체(Object)에 할당되어 있는 속성들을 가져다 쓸 수 있기에 더욱 효율적이라고 볼 수 있다.
function Mufasa() {
this.position = 'king';
}
function Simba() {}
Simba.prototype = new Mufasa();
Simba.prototype.position // King
Mufasa.prototype.rakecolor = 'orange';
Simba.prototype.rakecolor // orange
new
를 통해 객체를 생성할 경우 prototype을 이용하여 원본 주소로 찾아간다. 그렇기 때문에 하위 객체가 생성된 이후에 상위 객체에 속성을 추가/삭제한다고 해도 하위 객체의 속성 또한 자동으로 추가/삭제가 된다.function Mufasa() {
this.position = 'king';
}
function Simba() {}
Simba.prototype = Object.create(Mufasa.prototype)
Simba.prototype.position // undefined
Object.create()
를 통해 객체를 생성할 경우, 새로운 객체는 상위 객체의 prototype을 참조하는 것이 아닌, 상위 객체의 prototype을 prototype으로 하는 또 다른 객체에 대한 주소값을 참조하게 된다. 예시를 보았을 때, 새로 생성된 객체 Simba는 Mufasa.prototype을 참조하는 것이 아니라 Mufasa.protytpe을 prototype으로 하는 또 다른 객체를 참조한다. 한마디로 Simba가 참조하는 새로운객체의 prototype 주소와 Mufasa.prototype의 주소는 다른 주소인 셈이다.👉 상위 객체의 속성은 상속받으면서, 하위 객체 자신만의 속성을 추가하고 싶을 경우에는 Object.create()를 사용한다.