프로토타입1 글은 개념위주로 적었고 이번에는 코드를 좀 봅시다.
1. 연속 상속 ( = 채인닝 상속)
const A = {
name : 'A',
old : '10',
say(){
console.log(`Hello, I am A`);
}
}
const B = {
name : 'B',
old : '11',
say() {
console.log(`Hello, I am ${this.name}` )
},
__proto__ : A,
}
const C = {
name : 'C',
old : '12',
__proto__: B,
location : "seoul",
}
// 다음과 같이 호출하면 각각 뭐가 콘솔로 찍힐까요?
A.say();
B.say();
C.say();
한글 순서대로
"Hello, I am A"
"Hello, I am B"
"Hello, I am C"
로 나옵니다.
상속 관게를 보면 C -> B -> A 로 됩니다.
여기서 눈여겨 봐야할 것은 C가 B를 상속받았고, B는 A를 상속받았어도,
B와C에서는 say() 메서드를 호출했을때 Hello, I am A 가 나오지 않는다는 것 입니다.
이유는 객체에도 say() 메서드가 있고 그 객체의 프로토타입에도 같은 say()메서드가 있다면, 프로토타입의 있는 것이 무시되기 때문입니다.
따라서 B.say() 는 this.name으로 Hello, I am B 가 나오고
A.say()는 B를 상속받았으므로, this.name의 영향으로 마찬가지로 Hello, I am C가 출력됩니다.
또한 위처럼 프로토타입을 내려 받는 형식으로 상속이 이루어지며, 이렇게 줄줄이 연결이 되는 것들을 채이닝이라고 흔히 부릅니다. 줄줄이 연결되어 있다고 해서 입니다. 채이닝은 추상적인 개념이며, 여기서만 보이는게 아니라 다른 부분에서도 줄줄이 연결되는 부분이 있다면 채이닝이라고 볼 수 있습니다.
또 주의할 점은 "역참조는 되지 않는다" 입니다. 부모 객체에서 자식 객체로의 접근은 성립되지 않는다 입니다.
console.log(A.location); 를 하게 되면, C에는 location이 있지만 A에는 location이라는 프로퍼티는 없으므로 undefined가 나오게 됩니다.
2. 프로토타입 상속 관련 문제
const person = {
having : [],
take(something){
this.having.push(something);
// this.having = [something];
}
}
const A = {
__proto__ : person,
name : 'rich',
//having : [],
}
const B = {
__proto__ : person,
name : 'poor',
//having : [],
}
A.take("money");
console.log(A.having); //["money"]
console.log(B.having); //B는 아무것도 없어야 하는데 A랑 똑같이 money을 가지고 있다.
위코드를 잘보시면 A와 B 둘 다 person 객체를 상속받는다.
따라서 A.take() 를 호출한 순간 자바스크립트 엔진은 A에서 먼저 take() 메서드가 있나 확인한 후, 만약에 없으면 A의 프로토타입 안에서 찾아본다. 여기서 A의 프로토타입은 person이므로, 자바스크립트 엔진은 곧 person의 take() 메서드를 찾고 그것을 호출시킨다.
그런데 문제는 A와 B에는 having이라는 프로퍼티가 주석처리되었다. 따라서 this.having.push()를 만나게 되면 A와 B는 각각 having이 있는지 찾게되는데 둘 다 having이 없으므로, 프로토타입까지 가서 결국 person의 having을 찾아서 person의 having에 자신이 가진것을 배열로 저장하게된다.
때문에, A가 가진 money 도 having이라는 프로퍼티를 같이 참조하고 있기 때문에 B역시 가지게 되는 것이다.
이런 현상을 없애기 위해서는 각각 A,B 둘 다에게 having프로퍼티를 만들어 주는 것이다. 그러면 프로토타입까지 가서 having을 찾을 이유가 없으므로 각각의 having을 가지게 된다.
또 한가지 방법으로는
person에서 주석처리한 코드로 this.having.push()를 대체하는 것이다.
잘 보면 this.having.push()는 having안에 push라는 메서드가 있으므로 그것을 찾아 호출하라는 뜻이다.
즉 this.having.push()는 having이라는 프로퍼티가 이미 존재한다는 것에 가정한 코드이다.
하지만, this.having = [something]; 이 코드는 전혀 의미가 다르다.
이 코드는 만약 this가 가르키는 객체에 having이라는 프로퍼티가 있다면, 거기에 [something] 을 저장하라는 의미와
만약 this가 가르키는 객체에 having이라는 프로퍼티가 없다면, 새롭게 having이라는 프로퍼티를 만들고! 거기다가 [something]을 저장하라는 의미이다.
위에서는 A,B 둘다 having이 없으므로, 새롭게 having이라는 프로퍼티를 각각의 객체에 새롭게 만들어서 [something]이라는 배열을 저장하라는 의미로 작동하게 되고, 그렇게 되면 A, B둘 다 독립적이 자신만의 having프로퍼티를 가지므로, 서로 참조가 되어 A가 taking할 것을 B도 가질수 없다.
3. new 키워드와 생성자 함수
자바스크립트에서 일반적으로 객체를 만드는 방법은 두 가지입니다.
1. 리터럴 문법으로 직접 객체를 적어주는 법
2. 생성자 함수를 작성하여 new 생성자함수() 로 인스턴스를 찍어내는 것
코드로 보죠
// 리터럴 구문으로 직접 객체 작성
const obj1 = {
name : 'you nam seng',
old : '100',
location : 'seo-ul',
func1(){
console.log("hey I am Y.N.S");
}
}
// 생성자 함수를 정의하고 new키워드로 인스턴스 찍어내기
function Obj() {
this.name = 'you nam seng',
this.old = '100',
this.location = 'seo-ul',
this.func1 = function(){
console.log("hey I am Y.N.S");
}
}
// new Obj() 로 인스턴스 찍어내기
const obj2 = new Obj();
참고로 생성자함수의 이름을 작성할때는 암묵적인 약속이있는데 함수이름이지만 생성자함수로 쓰일 함수의 이름은 시작을 대문자로 하는것이 약속입니다.
위 둘의 방식은 모두 같은 객체를 만들고 있습니다. 이렇게 객체를 만드는데 2가지 방식이 있는데 이번에는 프로토타입과 관련하여 new키워드를 사용하는 방식에 대해서 좀 더 알아보죠
먼저 자바스크립트의 규칙이 있는데 이 부분은 그냥 일단은 외우시는게 좋습니다.
1. 자바스크립트에서 모든 함수는 자동으로 prototype이라는 프로퍼티를 가지게 된다.
2. 생성자 함수에도 prototype이라는 프로퍼티가 존재하고,
3. 이 prototype에 어떤 객체A를 넣어주면 해당 생성자함수로 생성하는 모든 인스턴스(객체)는 프로토타입으로 A을 참조하게된다.
3편에서 이어서 포스팅하겠습니다.
*틀린 점이 있다면 댓글로 달아주세요. 아직 배우는 학생이랍니다.
*질문도 댓글로 적어주시면 답변할 수 있는 한도 내에서 답변해드리겠습니다.
'javaScript > 초급(문법 위주)' 카테고리의 다른 글
자바스크립트 프로토타입 (JavaScript Prototype) 3편 (0) | 2020.10.20 |
---|---|
자바스크립트 프로토타입 (JavaScript Prototype) 1편 (개념위주) (1) | 2020.10.15 |
콜백 함수와 익명 함수에 관하여 (1) | 2020.10.14 |
Promise 자바스크립트 프로미스 비동기 통신 (0) | 2020.10.12 |
es6 객체 리터럴 문법 및 표현 (0) | 2020.10.09 |