프로토타입과 클래스에 알아보기 전에 객체 생성자를 알아야한다.
객체 생성자는 함수를 통해서 새로운 객체를 만들고 그안에 넣고 싶은 값 혹은 함수를 구현하게 해준다.
버통 객체 생성자를 사용 할 때 함수의 이름을 대문자로 하고, 새로운 객체를 만들 때에는 new 키워드를 앞에 붙여준다.
같은 객체 생성자 함수를 사용하는 경우, 특정 함수 또는 값을 재사용 할 수 있는데 바로 프로토타입이다.
function Person(name, age) {
this.name = name;
this.age = age;
this.introduce = function() {
console.log(`이름 : ${this.name} / 나이 : ${this.age}`);
};
}
const ahn = new Person('안씨', '34');
const kim = new Person('박씨', '43');
ahn.introduce(); //이름 : 안씨 / 나이 : 34
kim.introduce(); //이름 : 박씨 / 나이 : 43
JS에서 프로토타입이란, 객체의 특성을 다른 객체로 상속하는 것을 가능하게 하는 메커니즘이다.
[[Prototype]]은 내부 프로퍼티이기 때문에 특정 객체의 프로토타입 객체로 직접 접근하는 공식적인 방법은 존재하지 않는다. 하지만 대부분의 모던 브라우저에서 proto라는 비공식 프로퍼티를 지원하며, ES6부터 getPrototypeOf() 메서드를 통해 객체에 연결된 프로토타입 객체를 확인할 수 있다.
프로퍼티 탐색과정
1. 탐색을 시작한 객체에서 프로퍼티가 존재하지 않으면 연결된 프로토타입 객체로 이동하여 탐색한다.
2. 해당 프로토타입 객체에도 프로퍼티가 존재하지 않으면 그 객체와 연결된 프로토타입 객체로 이동하여 탐색한다. (프로토타입 객체도 자신의 프로토타입 객체를 가질 수 있다.)
3. 프로토타입 객체의 연쇄적인 흐름 속에서 프로퍼티를 찾았다면 그 값을 반환한다. 하지만 더 이상 연결된 프로토타입 객체가 없는 데도 프로퍼티가 존재하지 않는다면 탐색을 종료하고 undefined를 반환한다.
3번 과정에서 언급했다시피, 객체와 객체를 이어주는 링크는 한 개가 아닐 수도 있다. 이처럼 다수의 객체가 서로 링크로 연결되어 있다면 이 링크들의 연쇄적인 흐름을 Prototype Chain이라고 한다.
결국, 객체는 이 체인을 통해 자신이 가지고 있지 않은 특정 프로퍼티에 대한 참조를 다른 객체에 위임하여 해결할 수 있다.
JS에서 모든 Prototype Chain의 종착점에는 Object.prototype이라는 객체가 자리하고 있다. 이 객체는 모든 Built-in 객체들의 원형(原型) 객체이며, Built-in 객체들의 다양한 공통 특성들을 한 데 가진다. (원형의 의미를 가진 ‘프로토타입’이라는 단어가 사용된 이유를 여기서 짐작해볼 수 있겠다.)
Object.prototype과 연결된 객체마다 필요에 의하여 기능들을 상속받고 확장해 나간다. 그러한 과정에서 공통된 특성들은 하나의 프로토타입 객체에 모아 그것들을 참조하게 하여 메모리 자원을 보다 효율적으로 사용할 수 있다. 또한 프로토타입 객체에서 파생된 객체들의 프로퍼티를 수정하고 싶을 때, 그것들의 프로토타입 객체에만 선언함으로써 리팩토링의 강점을 취할 수도 있다.
프로토타입은 다음과 같이 객체 생성자 함수 아래에 .prototype.[원하는키] = 코드를 입력하여 설정 할 수 있다.
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.introduce = function() {
console.log(`이름 : ${this.name} / 나이 : ${this.age}`);
};
Person.prototype.gender = 'men';
const ahn = new Person('안씨', '34');
const kim = new Person('김씨', '43');
ahn.introduce(); //이름 : 안씨 / 나이 : 34
kim.introduce(); //이름 : 박씨 / 나이 : 43
console.log(ahn.gender); // men
console.log(kim.gender); // men
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.introduce = function () {
console.log(`이름 : ${this.name} / 나이 : ${this.age}`);
};
Person.prototype.gender = 'men';
function Ahn(age) {
Person.call(this, '안씨', age);
}
Ahn.prototype = Person.prototype;
function Park(age) {
Person.call(this, '박씨', age);
}
Park.prototype = Person.prototype;
const ahn = new Ahn(34);
const park = new Park(43);
ahn.introduce(); //이름 : 안씨 / 나이 : 34
kim.introduce(); //이름 : 박씨 / 나이 : 43
클래스라는 기능은 C++, Java, C#, PHP 등의 다른 프로그래밍 언어에는 있는 기능인데 자바스크립트에는 없었기 때문에 예전 자바스크립트 (ES5) 에서는 클래스 문법이 따로 없었기 때문에 위에서 작성한 코드처럼 객체 생성자 함수를 사용하여 비슷한 작업을 구현해왔다.
하지만 ES6 이후로 JS 내에서도 클래스 문법이 출현하였는데, 객체 생성자로 구현했던 코드를 조금 더 명확하고, 깔끔하게 구현 할 수 있게 해줍니다. 추가적으로, 상속도 훨씬 쉽게 해줄 수 있다.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduce() {
console.log(`이름 : ${this.name} / 나이 : ${this.age}`);
}
}
const ahn = new Person('안씨', '34');
const kim = new Person('김씨', '43');
ahn.introduce(); //이름 : 안씨 / 나이 : 34
kim.introduce(); //이름 : 박씨 / 나이 : 43
프로토타입 기반 언어에서는 클래스 - 인스턴스의 개념이 아닌 오직 객체 개념만 존재한다. 다시 말하지만, ES6에 도입된 클래스 문법은 실제 클래스 패턴에 대한 모방일 뿐이다. 이것은 JS의 생성자 함수 패턴을 좀 더 클래스답게 보이기 위한 장치이며 생성된 객체는 함수의 프로토타입 객체에 연결되어 있다.
class Person {}
const ahn = new Person();
console.log(ahn.__proto__);
// {constructor: class Person, __proto__: Object}
https://developer.mozilla.org/ko/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
https://learnjs.vlpt.us/basics/10-prototype-class.html
https://tecoble.techcourse.co.kr/post/2021-06-14-prototype/
https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/this%20%26%20object%20prototypes/ch5.md