자바스크립트는 본래 클래스라는 개념이 없었다 -> 상속 개념 없음
대신 프로토타입이 존재한다. -> 상속 개념을 흉내냄
최근 class가 추가 되었지만 자바스크립트는 여전히 프로토타입 기반이다.
function Person() {
this.job = true;
this.age = 23;
}
var kim = new Person();
var park = new Person();
console.log(kim.job); // => true
console.log(kim.age); // => 23
console.log(park.job); // => true
console.log(park.age); // => 23
kim, park은 모두 동일한 값을 가지고 있지만 메모리에는 각각 두개씩 4번 할당된다
이런 중복 할당을 해결하는게 프로토타입이다.
function Person() {}
Person.prototype.job = true;
Person.prototype.age = 23;
var kim = new Person();
var park = new Person():
console.log(kim.age); // => 23
...
Person.prototype이라는 빈 Object가 어딘가에 존재하고, Person 함수로부터 생성된 객체들은(kim, park) 어딘가에 존재하는 Object에 들어있는 값을 모두 갖다쓸 수 있다.
즉 kim, park이 같은 공간을 공유하는 개념이다.
자바스크립트에는 Prototype Link 와 Prototype Object가 존재하고 이 둘을 통틀어 Prototype이라고 부른다.
객체는 언제나 '함수'로 생성된다.
function Person() {} // => 함수
var personObject = new Person(); // => 함수로 객체를 생성
일반적인 객체 생성도 마찬가지다
var obj = {};
위의 객체 생성 역시 아래의 방법으로 생성된다. Object 함수는 자바스크립트에서 기본으로 제공된다.
var obj = new Object();
Object와 마찬가지로 Function, Array도 모두 함수로 정의되어 있다. 그런데 이게 Prototype Object와 무슨 관련이 있을까?
함수가 정의될 때 2가지 일이 동시에 일어나는데 그 중 하나가 생성자 자격이다.
생성자 자격이 부여되면 new를 통해 객체를 만들 수 있게 된다.
(함수만이 new 키워드 사용가능한 이유!)
obj 는 Object(생성자) 함수로 만들어진 객체일 뿐
함수가 아니기 때문에 new 키워드로 객체를 만들 수 없음
드디어 Prototype Object가 등장했다'
생성자 자격 부여와 동시에 Prototype Object가 생성되고
각각 prototype과 constructor으로 서로에게 접근 할 수 있다.
이때 생성된 Prototype 객체는 일반 객체와 같다
위에서의 예시를 다시 살펴보자
function Person() {}
Person.prototype.job = true;
Person.prototype.age = 23;
var kim = new Person();
var park = new Person():
console.log(kim.age); // => 23
...
Prototype Object는 일반적인 객체이므로 속성을 마음대로 추가/삭제 할 수 있다. kim과 park은 Person 함수를 통해 생성되었으니 Person.prototype을 참조할 수 있게 된다.
🚨 그런데 어떻게 참조할 수 있는걸까?
(나의 속마음: 아무리 kim,park이 Person함수로 생성됐다지만
kim.Person.prototype.age 가 아니라 kim.age 로 값을 바로 불러 올 수 있을까?)
Person 함수가 가진 prototype 속성은 함수만 가질 수 있다면
Person Prototype Object가 가진 __proto__는 모든 객체가 가진 속성이다.
__proto__는 객체가 생성될 때 조상이었던 함수의 Prototype Object를 가리킨다. (예시에선 Person Prototype Object을 가리킴)
이때 kim객체는 eyes를 직접 가지고 있지 않기 때문에 eyes 속성을 찾을 때 까지 상위 프로토타입을 탐색한다.
이렇게 __proto__속성을 통해 상위 프로토타입과 연결되어있는 형태를 프로토타입 체인(Chain)이라고 한다.
최상위인 Object의 Prototype Object까지 도달했는데도 못찾았을 경우 undefined를 리턴한다.
이런 프로토타입 체인 구조 때문에 모든 객체는 Object의 자식이라고 불리고, Object Prototype Object에 있는 모든 속성을 사용할 수 있다.
function Animal (name, sound){
this.name = name;
this.sound = sound;
}
Animal.prototype.getInfo = function(){
return this.name + '가(이)' + this.sound + ' 소리를 냅니다.';
}
function Friends(name, sound){
Animal.call(this, name, sound);
}
이때 call() 함수의 첫번째 매개변수는 다른 곳에서 정의된 함수를 현재 컨텍스트에서 실행할 수 있도록 한다.
const dog = new Friends('개', '멍멍');
const cat = new Friends('고양이', '냥');
console.log(dog.getInfo()); // getInfo() is not a function
console.log(cat.getInfo()); // getInfo() is not a function
그런데 왜 getInfo 메서드를 사용할 수 없을까?
const isEmptyFriends = Object.getOwnPropertyNames(Friends.prototype);
const isEmptyAnimal = Object.getOwnPropertyNames(Animal.prototype);
console.log(isEmptyFriends); // [ 'constructor' ]
console.log(isEmptyAnimal); // [ 'constructor', 'getInfo' ]
확인 결과 Friends의 프로토타입 객체는 기본 constructor 속성 말고는
Animal 프로토타입 객체의 메서드를 상속 받지 못한 것 을 볼 수 있다.
(constructor 속성 덕분에 기본 Animal 속성은 접근 가능)
1 ) Object.create로 Animal의 프로토타입 객체를 Friends의 프로토타입 객체로 설정한다.
Friends.prototype = Object.create(Animal.prototype);
console.log(Friends.prototype.constructor); // Animal
2 ) 생성자 속성을 Friends로 바꾸기
Friends.prototype.constructor = Friends;
function Animal (name, sound){
this.name = name;
this.sound = sound;
}
Animal.prototype.getInfo = function(){
return this.name + '가(이)' + this.sound + ' 소리를 냅니다.';
}
function Friends(name, sound){
Animal.call(this, name, sound);
}
Friends.prototype = Object.create(Animal.prototype);
Friends.prototype.constructor = Friends;
const dog = new Friends('개', '멍멍');
const cat = new Friends('고양이', '냥');
console.log(dog.getInfo()); // 개가(이)멍멍 소리를 냅니다.
console.log(cat.getInfo()); // 고양이가(이)냥 소리를 냅니다.