- prototype, __proto__, constructor의 관계와 프로토 타입 체인 정리
- Object.create() 정리
- ES6의 Class 키워드와 super 키워드 정리 및 psedoclassical 과의 비교를 통한 프로토타입 체인 정리
Object.create
Object.create 메소드는 인자로 받은 객체를 프로토타입 객체로하는 새로운 객체를 만드는 메소드 이다. (MDN 참고)
let temp = {
first : "hi"
}
위의 temp 는 객체로 first를 프로퍼티로 갖고 있다. 그리고 객체이기 때문에 __proto__를 갖고있고 이것은 최상위인 Object의 프로토타입을 가리키고 있다.
여기에서 새로운 객체를 Object.creat를 통해 만들어 보겠다.
let obj = Object.create(temp);
obj.first; // "hi"
여기에서 obj를 까보면 obj 자체로는 first라는 프로퍼티를 갖고 있지 않다. 하지만 obj.first를 "hi"라는 값이 나오는 것을 알 수 있는데 이것은 obj가 Object.create 메소드를 통하여 temp를 프로토타입 객체로 하는 객체로 만들어졌기 때문이다.
obj의 __proto__를 보면 temp 객체가 들어있는 것을 알 수 있다.
Subclassing - psedoclassical
이전까지는 instance가 만들어질 때 class로부터 프로토타입을 기반으로 메소드와 프로퍼티를 상속 받는 것을 알아 보았다.
그러면 이제는 어떤 class가 다른 상위 class의 프로퍼티와 메소드를 상속 받는 것을 알아 보겠다.
function Human(name) {
this.name = name
}
Human.prototype.eat = function() {
console.log("밥을 먹는다")
}
function Student(name) {
// 어떻게 해야할까?
}
let student1 = new Student("kim");
Human 이라는 클래스가 있다. 이 클래스는 이름을 프로퍼티로 가지고 있고, eat라는 메소드가 있다.
여기에서 Student라는 새로운 클래스를 만든다. 학생 또한 인간이기 때문에 마찬가지로 이름을 가지고 있고 eat라는 메소드를 사용할 수 있어야 한다. 그러면 이렇게 학생이라는 클래스가 인간이라는 클래스는 상속받으려면 어떻게 해야 할까?
psedoclassical 방식에서는 먼저 Student라는 함수가 Human의 프로토타입을 참조 할 수 있도록 해야한다.
Student.prototype = Object.create(Human.prototype);
Student의 프로토타입은 컨스트럭터와 __proto__를 갖고 있을 것이고, 컨스트럭터는 Student를 가리키고 있을 것이고 __proto__은 최상위인 Object의 프토로타입을 가리키고 있었을 것이다.
그런데 Object.create 메소드를 통하여 Student 의 프로토타입 객체가 Human의 프로토타입을 프로토타입으로 가지는 객체로 만들어 준다. 이렇게 해주면 Student 의 프로토타입의 __proto__는 Human의 프로토타입을 가리키기 때문에 eat 메소드를 사용할 수 있게 된다.
(이때 Human의 프로토타입 '객체'를 인자로 넣어줘야한다!! Human만 넣어버리면 함수 자체를 프로토타입으로 하는 것이기 때문에..안됨)
하지만 위의 과정 중에서 Student의 프로토타입이 원래 가지고 있던 컨스트럭터가 날라가게(?) 된다. 왜냐하면 객체를 만들어서 할당해 줬기 때문이다. 그래서 constructor를 찍으면 없기 때문에 __proto__를 통해 Human의 컨스트럭터를 참조하게 된다.
그래서 Student의 컨스트럭터를 다시 만들어주어야 한다.
Student.prototype.constructor = Student;
그런데 만약 Student의 프로토타입을 Object.create 메소드를 쓰지 않고 그냥 Human의 프로토타입을 할당하면 어떻게 될까??
이것은 Student의 프로토타입이 Human의 프로토타입을 프로토타입을 통해 참조하는 것이 아니라 직접(?) 참조하기 때문에 만약 Student의 프로토타입에 메소드를 추가하면 Human의 프로토타입에도 메소드가 추가 되버린다. 그렇기 때문에 프로토타입 체인을 통하여 참조하도록 해주는 것이다. (객체의 깊은 복사를 하는 느낌!!!)
여기까지 해주면 컨스트럭터도 올바르게 가지고 있고, 상위 class의 메소드 등도 올바르게 상속 받고 있다. 하지만 프로토타입 객체를 상속받는다고 해서 그 프로퍼티까지는 아직 상속 받고 있지 않다. (프로토타입에는 메소드만 들어있기 때문에)
그렇기 때문에 프로퍼티까지 상속 받기 위해서는 다음과 같은 작업이 필요하다.
funtion Student(name) {
Human.call(this, name) // Human.apply도 가능
}
이렇게 3가지의 과정을 거치면 Student가 Human을 상속 받아 작동할 수 있게 된다.
function Human(name) {
this.name = name
}
Human.prototype.eat = function() {
console.log("밥을 먹는다")
}
function Student(name) {
Human.call(this, name)
}
Student.prototype = Object.create(Human.prototype);
Student.prototype.constructor = Student;
subclassing - ES6 class, super 키워드
앞서 psedoclassical 방식은 상속을 구현하는데 번거로운 과정들이 있다. 그래서 ES6 에서의 class 키워드와 super 키워드를 통하여 이 과정을 좀 더 깔끔하게 구현할 수 있다.
class Human {
constructor(name) {
this.name = name;
}
eat() {
console.log("밥을 먹는다")
}
}
class Student extends Human {
constructor(name) {
super(name)
}
}
class 키워드를 이용하여 클래스를 만들고 하위 클래스에서 extends를 하여 상속 받을 클래스를 지정한다.
그리고 컨스트럭터 안에 super를 이용하면 위의 과정이 모두 한번에 구현이 된다.
Polymorphism(다형성)
앞서 방법들을 통해 상속을 구현할 수 있다. 이때 Student는 eat 메소드를 통해 밥을 먹는 것도 할 수 있지만 다른 것도 먹고 싶으면 어떻게 할까?? 이러한 개념을 OOP에서 다형성이라고 한다. 다형성이 상속을 통해 어떻게 구현되는지 알아보았다.
class Human {
constructor(name) {
this.name = name;
}
eat() {
console.log("밥을 먹는다")
}
}
class Student extends Human {
constructor(name) {
super(name)
}
eat() {
super.eat()
console.log("비타민을 먹는다")
}
}
Student는 기존의 Human의 eat 메소드를 상속받아서 "밥을 먹는다"는 콘솔에 동일하게 찍힌다. 이때 Student에 eat 메소드를 새로 만들면서 "비타민을 먹는다"를 콘솔에 찍도록 만들어줄 수 있다. 하지만 이것만 넣으면 비타민을 먹는다를 찍는 것으로 새로 만들어준 것이기 때문에 밥을 먹는다는 찍히지 않는다. 이때 eat 메소드 안에 super를 이용하면 기존의 상속받은 eat를 실행하고 비타민을 먹는다도 실행되기 때문에 둘 다 나오게 된다.
이렇게 상위 class를 상속 받고 자기 자신만의 특별한 것을 새로 넣어줌으로써 다형성의 개념을 구현 할 수 있다.
(프로퍼티도 마찬가지로 방법으로 컨스트럭터 안에 구현하여 가능)
참고로 psedoclassical 방식에서는
Student.prototype.eat = function() {
Human.prototype.eat.call(this)
console.log("비타민을 먹는다")
}
프로퍼티를 상속 받는 것처럼 Human의 eat을 call을 통해 호출하는 방식으로 구현이 가능하다.
'JS' 카테고리의 다른 글
(191222) Common JS & ES6 Modules (0) | 2019.12.22 |
---|---|
(191127) Instantiation & Inheritance (1) (0) | 2019.11.27 |
댓글