📌 "ProtoType 기반 상속에 대해 설명해주세요."
JavaScript는 객체지향 언어이지만 다른 언어와 다르게 class
가 존재하지 않는다.
따라서, JavaScript는 class
에 있는 상속 기능이 존재하지 않는다.
대신 자신의 부모 역할을 담당하는 객체와 연결하여 상속 등을 흉내낼 수 있다.
이러한 부모 객체를 Prototype 객체 혹은 Prototype 이라고 한다.
이런 Prototype은 JavaScript의 모든 객체는 객체가 정의됬을 때 자동으로 생성되고 초기화된다.
📖 JS에서 객체를 생성하면
[[Prototype]]
이라는 은닉된 프로퍼티을 가지고 자신의 Prototype이 되는 다른 객체를 가르킨다.
Prototype을 사용함으로서 아래의 장점을 가질 수 있다.
💡 프로퍼티(property) : 객체 내부의 속성()으로
"키": "값"
으로 구성된다.
⚠️ JavaScript는 객체지향 언어라면 당연히 있어야할 클래스가 없다.
ES6 에class
가 추가되어도 문법으로서 추가된 것이지 클래스 기반이 아닌 Prototype 기반 언어이다.
아래와 같이 프로퍼티를 공유할 수 있다.
var A = { a: "hi" };
var B = { b: "hello" };
// A의 __proto__ 는 B를 가리키게 된다. 즉, A의 부모가 B가 된다.
// ⚠️ 그러나 실제로 A에게 B.b 를 주는 것이 아니다.
// 이는 프로토타입 체인을 통해 해당 값을 찾아가는 것이다. (이는 아래의 나중에 설명하겠다.)
A.__proto__ = B;
console.log(A.b); // "hello";
B.b = "bye bye";
console.log(A.b); // "bye bye";
__proto__
와 prototype
__proto__
prototype
function Person(name) {
this.name = name;
}
var foo = new Person('Lee');
console.dir(Person); // prototype 프로퍼티가 있다.
console.dir(foo); // prototype 프로퍼티가 없다.
console.log(foo.__proto__ === Person); // false
// __proto__ 는 부모 객체의 프로토타입을 가리킨다.
console.log(foo.__proto__ === Person.prototype); // true
// 함수도 객체이기 때문에 함수의 프로토트입의 __proto__ 는 Object의 prototype 을 가리킴
console.log(Person.prototype.__proto__ === Object.prototype); // true
⚠️
__proto__
은 legacy 이다.
대신에Object.getPrototypeOf()
,Object.setPrototypeOf()
을 사용해야한다.
📖
__proto__
의 역사
- 생성자 함수의
prototype
프로퍼티는 아주 오래전부터 그 기능을 수행하고 있었다.- 그런데 2012년, 표준에
Object.create
가 추가되면서 주어진 프로토타입을 사용해 객체를 만들 수 있긴 하지만, 프로토타입을 얻거나 설정하는것은 불가능했다.
그래서 브라우저는 비표준 접근자인__proto__
를 구현해 언제나 프로토타입을 얻거나 설정할 수 있도록 하였다.- 이후 2015년에
Object.setPrototypeOf
와Object.getPrototypeOf
가 표준에 추가되면서__proto__
와 동일한 기능을 수행할 수 있게 되었다.
C++, JAVA와 같은 언어는 객체를 만든 시점에서 상속구조를 통해 어떤 멤버들을 가지고 있는지 이미 알고 있다.
이와 다르게 JavaScript는 식별자를 찾는(룩업) 방법으로 두 종류의 메커니즘을 사용하여 원하는 식별자를 찾을 때까지 상위 객체를 참조한다.
💡 식별자 : 변수, 상수, 함수, 클래스 등의 이름
💡 프로토타입 체인 : 어떤 객체의 프로퍼티 를 찾아내는 메커니즘
💡 스코프 체인 : 어떤 객체(전역 객체가 아닌)의 프로퍼티 이 아닌 식별자를 찾아내는 메커니즘
var a = {
attr1: 'a'
};
var b = {
__proto__: a,
attr2: 'b'
};
b.attr1; // 'a'
attr1
속성을 찾는다. -> 없다.__proto__
속성이 존재하는지 확인한다. -> 있다.__proto__
속성이 참조하는 객체로 이동한다. -> a객체로 이동attr1
속성을 찾는다. -> 있다.undeifned
를 반환한다.Prototype으로 생성자를 Object.prototype.constructor
를 이용하여 만들 수 있다.
var obj = {};
console.log(obj.constructor === Object.prototype.constructor); // true
console.log(obj.constructor === obj.__proto__.constructor); // true
생성자를 이용하여 객체를 생성하면 객체는 생성자의 프로토타입 객체와 프로토타입 체인 으로 연결된다.
아래의 예제를 통해 이를 확인해보자
// 생성자
function Student(str) {
this.name = str;
}
Student.prototype.getName = function() {
return this.name;
};
var std = new Student('GilDong');
위 코드는 JS 엔진에서 아래와 같이 진행된다.
function Student(str) {
this.name = str;
}
// 호이스팅
var std;
std = {}; // 새 객체를 만듦
Student.call(std, "GilDong"); // call을 사용하여 Student 의 this를 설정 및 함수를 호출
std.__proto__ = Student.prototype; // 프로토타입을 연결함
std.getName(); // "GilDong" 출력
상속을 하는 법은 크게 2가지로 나뉘어진다.
Object.create()
사용(이것도 결국 Prototype 을 이용)
var a = { attr1: 'a' };
var b = Object.create(a);
b.attr2 = 'b';
Prototype 을 이용
var a = { attr1: 'a' };
function Ghost() {}
Ghost.prototype = a;
var b = new Ghost();
b.attr2 = 'b';
좀 더 어려운 예
function Parent(num) {
this.num = num;
}
Parent.prototype.getNumber = function() {
return this.num;
};
function Child(num, name) {
// call을 사용해 Parent의 this를 바인딩 및 num으로 인자로 주어 호출
Parent.call(this, num);
this.name = name;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.getName = function() {
return this.name
}
var c = new Child(1, "Hong Gil Dong");
console.log(c.getNumber()); // 1
console.log(c.getName()); // "Hong Gil Dong"
Prototype이란 자신의 부모 역할을 담당하는 객체이다.
JavaScript는 Class
가 존재하지 않지만 Prototype을 이용하여 Class
처럼 비슷하게 구현할 수 있다.
Class
가 존재하지만 JavaScript가 Class
기반의 언어가 된 것은 아니다객체가 정의됬을 때 자동으로 Prototype이 생성되고 초기화된다.
JavaScript는 식별자를 찾는(룩업) 방법으로 프로토타입 체인 을 통해 원하는 속성이 나올때까지 계속 객체의 메서드나 속성을 찾아나간다.