도도의 IT이야기
prototypical inheritance 본문
복습시작
그전에 new의 역활을 정리하자면
1. 비어있는 객체를 만든다 {}
2. 생성자의 this를 비어있는 객체에 bind한다
3. 비어있는 객체에 __proto__라는 속성을 만든다. __proto__는 객체를 만든 생성자의 프로토타입 속성을 포인트한다.
4. 생성자가 비어있는 객체에 새로운 속성을 파라미터로 받은 값으로 지정해 준 다음에 this(객체)를 return한다
다른 역활을 간단한지만 3번은 이해하기가 어렵다 그러니 그림으로 표현해보자
이렇게 모든 객체안에는 __proto__라는 속성이 있고 이 __proto__는 상위 객체에서 상속받은 속성들을 나타낸다.
이거 두 개만 기억하자
1. __proto__가 가리키는 속성들이 곧 객체에 상속되는 속성들이다.
2. literal notation과 constructor으로 생성된 객체들은 __proto__가 자동적으로 생성자의 프로토타입 속성을 가리키지만 Object.create()를 이용해서 __proto__가 가리킬 객체를 정의할 수 있다.
dog1 과 Dog.prototype 둘다 construtor으로 생성된 객체이다. 그래서 __proto__는 각각의 생성자의 프로토타입 속성을 가리킨다.
그래서 dog1은 Dog.prototype(자신의 생성자의 프로토타입 속성)을 상속하고
그래서 Dog.prototye 또한 Object.prototype(자신의 생성자의 프로토타입 속성)을 상속한다.
Supertype 그리고 inheritance
이제 이런 상황을 생각해봅시다.
function Cat(name){
this.name = name;
}
Cat.prototype = {
constructor:Cat,//reinitialize 하는거기 때문에 기존의 constructor속성이 지워진다 다시 쳐야됨
numLegs : 4,
sound : function(){return "냐옹냐옹";},
eat : function(){return '냠냠';},
friends : ['charlie','john']
}
function Dog(name){
this.name=name;
}
Dog.prototype = {
constructor : Dog,
numLegs : 4,
sound : function(){return "멍멍";},
eat : function(){return '냠냠';},
friends : ['charlie','john']
}
let cat1 = new Cat('Alex');
let dog1 = new Cat('Ashley');
우리는 이렇게 Cat 생성자만 아니라 Dog 생성자도 추가했습니다.
Cat object들은 모두 Cat.prototype을 상속하고
Dog object들은 모두 Dog.prototype을 상속합니다.
근데 Dog.prototype과 Cat.prototype은 공통된 속성들이 존재합니다. 여기서 우리는 같은 코드를 두번 반복하고 있으니 좋은 코드가 아닙니다.
어? 우리가 dog1 dog2객체에 반복되는 코드가 있어서 Dog 생성자를 말들었죠? Dog.prototype과 Cat.prototype 사실 객체일 뿐입니다 그래서 그들을 만드는 생성자를 만들면되죠.
그렇기에 우리는
1. 두 생성자의 프로토타입 객체를 생성하는 상위 생성자를 만들어야합니다.
2. 그리고 두 생성자의 프로토타입 객체의 공통 속성을 상위 생성자의 프로토타입 속성에 넣어둡니다.
3. 상위 생성자를 호출시켜 두 생성자의 프로토타입 객체를 initialize 합니다. Own property인 constructor도 초기화 됬으니 직접 넣어주도록 합시다.
^본인이 굉장히 헷갈렸기에 프로토타입 객체가 아닌 일반 객체를 생성하는 생성자를 만드는 방법과 비교해봄
일반 객체 생성자 만들때 순서
car1 = {
wheels:4,
color:'white',
engine:function(){return 'vroom';},
charge:'75%'
}
car2 = {
wheels:4,
color:'blue',
engine:function(){return 'vroom';},
gasoline:'12L'
}
1. 반복되는 속성들이 많은 관련된 객체를 발견
2. 그들을 생성하는 constructor을 만들어야겠다는 생각을 함
3. 우선 만들기 전에 속성들을 나누자. A) 속성과 값 전부 다 같은 속성 B) 속성은 같지만 값은 다른 속성 C)둘다 다른 속성
여기선 wheels, engine이 A속성, color가 B속성 charge,gasoline이 C속성
4. 이제 constructor을 만들자
function CarMaker(){
}
CarMaker.prototype = {
wheels:4,
engine:function(){return 'vroom'}
}
속성과 값이 아예 같은 거는 생성자의 프로토타입에 나둔다.
function CarMaker(color){
this.color = color;
}
CarMaker.prototype = {
wheels:4,
engine:function(){return 'vroom'}
}
값만 다른것은 값을 파라미터로 받아 객체의 속성에 initialize하는 expression을 만들자.
function CarMaker(color){
this.color = color;
}
CarMaker.prototype = {
wheels:4,
engine:function(){return 'vroom'}
}
let car1 = new CarMaker('black');
let car2 = new CarMaker('blue');
car1.charge = '75%';
car2.gasoline = "12L";
값과 속성이 둘다 다른것은 객체를 만들고 따로 추가하자.
이제 위의 순서를 따라서 프로토타입 객체를 생성하는 생성자를 만들어보자
function Cat(name){
this.name = name;
}
Cat.prototype = {
constructor:Cat,//reinitialize 하는거기 때문에 기존의 constructor속성이 지워진다 다시 쳐야됨
numLegs : 4,
sound : function(){return "냐옹냐옹";},
eat : function(){return '냠냠';},
friends : ['charlie','john']
}
function Dog(name){
this.name=name;
}
Dog.prototype = {
constructor : Dog,
numLegs : 4,
sound : function(){return "멍멍";},
eat : function(){return '냠냠';},
friends : ['charlie','john']
}
let cat1 = new Cat('Alex');
let dog1 = new Dog('Ashley');
1. 반복되는게 많은 객체들을 발견!
2. 그들을 만드는 생성자를 만들어야겠다는 생각을함
3. 우선 만들기 전에 속성들을 나누자. A) 속성과 값 전부 다 같은 속성 B) 속성은 같지만 값은 다른 속성 C)둘다 다른 속성
A속성: numLegs, eat, friends B속성: constructor, eat
4. 이제 constructor을 만들자!
function Animal(func,cry){
this.constructor=func;
this.sound=function(){return cry};
}
Animal.prototype={
constructor:Animal,//초기화하는 거기 떄문에 넣어줘야됨
numLegs : 4,
eat : function(){return '냠냠';},
friends : ['charlie','john']
}
function Cat(name){
this.name = name;
}
Cat.prototype = new Animal(Cat, '냐옹냐옹');
function Dog(name){
this.name=name;
}
Dog.prototype = new Animal(Dog, '멍멍');
let cat1 = new Cat('Alex');
let dog1 = new Dog('Ashley');
이제 dog1의 포로토타입은 Dog.prototype이고
Dog.prototype의 프로토타입은 Animal.prototype
Animal.prototype의 프로토타입은 Object.prototype이다.
다르게 말하면 각 __proto__ 속성이 자신의 생성자의 프로토타입 속성을 가리키고 있는것.
용어정리!
Animal 같은 상위 생성자를 super class/ super type
Dog 같은 하위 생성자를 sub class/ sub type 이라고 부름
왜인지는 모르겠지만 내가 위에 친 코드는 안쓰임.
지금 부터 이유를 알려줌.
Object.create()
Object.create()는 Object 생성자의 정적 메소드이다.
객체를 생성할떄 사용된다
let newObj = Object.create(newObj의 프로토타입, own Property로 추가될 속성들)
또는
let newObj = Object.create(newObj이 상속할 속성들이 있는 객체/newObj의 __proto__가 가리킬 객체, 추가될 속성들)
Object.create의 첫번쨰 특이점: 첫번쨰 인자로 객체의 프로토타입을 지정할 수 있다
지금까진 새로운 객체를 만들면 새로운 객체의 프로토타입은 무조건 생성자의 프로토타입 속성이었다. 즉 __proto__가 constructor.prototype을 가리키고 있었던것
하지만 Object.create()를 사용하면 프로그래머가 상속받을 객체를 정할 수 있다. 예를 들어
let character = {
power : 'strong',
hp :100,
skill : function(){return '회전회오리'}
}
char1 = Object.create(character);
//결과
char1 = {
__proto__:character
}
이렇게 프로토타입 객체가 아닌 객체도 상속받을 수 있었다.
원한다면 아예 아무것도 상속받지 않게 할수 있다.
let newObj = Object.create(null)//이 객체는 Object.prototype을 상속받지 않음
Object.create의 두번째 특이점: 두번째 인자로 객체의 own property를 지정할 수 있다
첫번째 인자로 프로토타입을 지정할 수 있는건 알겠다. 근데 프로토타입 말고 own property를 지정하고 싶으면 두번쨰 인자로 보내면된다 하지만 특별한 룰을 따라야됨.
foo: { writable: true, configurable: true, value: 'hello' }
Object.create의 세번째 특이점: constructor 함수를 실행시키지 않는다
function People(name,age){
this.name = name;
this.age = age;
}
People.prototype.isHappy = true;
People.prototype.hands = 2;
let person1 = Object.create(People.prototype);
//결과
person = {
__proto__:People.prototype;
}
보이다 시피 우리는 People의 새로운 인스턴스인 person1을 만들었다.
하지만 Object.create를 사용해 People constructor을 거치지 않고 그냥 프로토타입 속성만 상속한 객체를 만들었다.
이제 내가 위에 친 코드를 Object.create로 고쳐볼것이다.
function Animal(func,cry){
this.constructor=func;
this.sound=function(){return cry};
}
Animal.prototype={
constructor:Animal,//초기화하는 거기 떄문에 넣어줘야됨
numLegs : 4,
eat : function(){return '냠냠';},
friends : ['charlie','john']
}
function Cat(name){
this.name = name;
}
Cat.prototype = new Animal(Cat, '냐옹냐옹');
function Dog(name){
this.name=name;
}
Dog.prototype = new Animal(Dog, '멍멍');
let cat1 = new Cat('Alex');
let dog1 = new Dog('Ashley');
Cat.prototype과 Dog.prototype을 Object.create를 사용하여 만들어보자
function Animal(){}
Animal.prototype={
constructor:Animal,//초기화하는 거기 떄문에 넣어줘야됨
numLegs : 4,
eat : function(){return '냠냠';},
friends : ['charlie','john']
}
function Cat(name){
this.name = name;
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.sound= function(){return '냐옹냐옹';};
function Dog(name){
this.name=name;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Cat;
Dog.prototype.sound= function(){return '멍멍';};
let cat1 = new Cat('Alex');
let dog1 = new Dog('Ashley');
이렇게 바꿀 수 있다.
왜인지는 모르겠지만 Super class로 Sub class를 만들때에는 이방법을 더 많이 쓰는것 같다.
Override
override는 어떠한 속성을 두번 initialize하는 것입니다. 자바스크립트는 프로토차입 체인위로 올라가다 먼저나온 속성을 택합니다.
예를 들자면,
function Parent(){}
Parent.prototype.talk = function(){return 'HEY!'};
function Child(){}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.talk = function(){return 'MAMA!'};
Child.prototype.constructor = Child;
let exObj = new Child();
exObj.talk();//MAMA!
보이다 시피 프로토타입 체인 위로 올라가던중 먼저나온 Child.prototype.talk가 실행되었다. 그렇기에 Sub class 중 한개만 다른 속성값을 가지고 싶게 하고싶다면 위처럼 하면 된다.
'IT > 자바스크립트' 카테고리의 다른 글
[jQuery] jquery와 javascript의 차이점 (0) | 2020.05.11 |
---|---|
OOP Mixin과 클로져를 이용한 private 속성 (0) | 2020.05.11 |
[자바스크립트 기본]객체,생성자,프로토타입,프로토타입 체인 (1) | 2020.05.08 |
Object.prototype.toString() 메서드 (0) | 2020.05.07 |
[자바스크립트 기본] 문법과 리터럴 (0) | 2020.05.07 |