성장을 위한 기록

중급 기본 정리 (with 앙마코딩) 상속과 Prototype 본문

FE (Front End) (구)/javascript

중급 기본 정리 (with 앙마코딩) 상속과 Prototype

B_Tae 2022. 3. 17. 12:57

이 글은 인프런에서 앙마코딩님의 무료 강좌를 학습한 내용입니다. (자바스크립트 중급 강좌)
문제 시 바로 삭제하겠습니다.

출처 [무료] 자바스크립트 중급 강좌 대시보드 - 인프런 | 강의 (inflearn.com)

상속과 Prototype

객체에서 자신이 프로퍼티를 가지고 있는지 확인 하는 프로퍼티가 있다. 이는 hasOwnProperty 이다.

const my = {
  name : 'Btae',
};

// my.name > 'Btae'
//my.hasOwnProperty('name') > true
//my.hasOwnProperty('age') > false

이렇게 객체에 있다면 true, 없다면 false를 반환한다.

 

하지만 우리는이 hasOwnProperty프로퍼티를 만든적이 없다. 그렇다면 어디에 존재하나?
바로 __proto__에 존재한다. 객체 프로퍼티를 찾을 때 먼저 객체 내부를 찾고 없으면 __proto__안을 찾게 된다.

 

그렇다면 만약 객체 프로퍼티에 hasOwnProperty가 있다면?

const my = {
  name : 'Btae',
  hasOwnProperty : function(){
    console.log('hello,');
};

// my.name > 'Btae'
//my.hasOwnProperty('name') > 'hello,'
//my.hasOwnProperty('age') > 'hello,'

기존에 사용하던 기능을 잃고 하나의 키값으로 프로퍼티를 반환하게 된다.
이는 위에 언급했듯 찾는 과정에서 객체 내부를 먼저 확인하고 __proto__를 확인하는데 객체 내부에 키값으로 존재하기 때문에, 먼저 반환하고 더 이상 찾지 않는 것이다.

 

나는 이 __proto__상속으로 이해했다.
예를 들어보자

const k3 = {
  name: 'k3',
  color : 'red',
  wheels: 4,
  drive() {
    console.log('drive');
  },
};

const k5 = {
  name : 'k5',
  color : 'black',
  wheels: 4,
  drive() {
    console.log('drive');
  },
};

const k9 = {
  name:'k9',
  color:'white',
  wheels: 4,
  drive() {
    console.log('drive');
  },
};

 

이렇게 자동차 모델에 정보를 갖는 객체가 있는데, 공통되는 부분이 많아 이를 효율적으로 작성하고 싶을 때 상속의 개념을 이용할 수 있다.

간단하게 변경을 해보면

 const car = {
  wheels: 4,
  drive() {
    console.log('drive');
  },
};

const k3 = {
  name: 'k3',
  color : 'red',
};

const k5 = {
  name : 'k5',
  color : 'black',
};

const k9 = {
  name:'k9',
  color:'white',
};

k3.__proto__ = car;
k5.__proto__ = car;
k9.__proto__ = car;

이렇게 공통되는 부분을 따로 car라는 객체로 만들어 이 객체를 각각의 객체에 상속을 하는 것이다.
따라서 k3, k5, k9은 .__proto__를 통해 각각 car객체를 상속받은 것이다.

 

위 코드를 콘솔창에서 확인을 해보면

k3 > {name:'k3', color: 'red'}
k3.name = 'k3'
k3.wheels = 4

k3.hasOwnProperty('age') > false
k3.hasOwnProperty('wheels') > false

이 예시를 살펴보자. 우선 k3객체는 변하지 않았다 그 이유는 상속 받은 객체는 앞서 언급함 __proto__안에 존재하기 때문이다.
그래서 객체 내에는 없지만, k3.wheels를 확인해보면 상속 받은 값 4가 반환된다.

 

그런데 객체 내에 프로퍼티가 있는지 찾는 hasOwnProperty를 사용하면 false가 나타난다.
이유는 hasOwnProperty는 객체 내에서만 확인을 하고 __proto__는 확인하지 않기 때문이다.

 

상속은 계속 할 수 있다.

/*앞선 예제 코트 ....
... k3..__proto__ = car;*/

const k3_hev = {
  moter : 'hev',
};

k3_hev.__proto__ = k3;

위 코드처럼 상속 받았던 k3를 다른 객체에 상속할 수 있다.
단, 상속된 객체는 한 객체에 같이 있는 것이 아니다. 쉽게 말해서 우리는 상속된 객체를 콘솔창에서 __proto__으로 확인 할 수 있었다. 이 __proto__가 중첩되어 쌓인다고 생각하면 된다.
이 개념이 필요한 이유는 객체에 프로퍼티를 찾는 과정이 있기 때문이다. 앞서 말했듯 객체 내에서 먼저 찾고 없으면 상속된 __proto__에서 찾게 된다. 그리고 나중에 상속된 객체가 최상의 상속 객체가 된다.

 

위 예제를 보며 설명을 해보면 k3_hev.wheels를 찾는다 가정을 해보자. 우선 k3_hev 자체 객체 내에서 찾게된다. 이 객체에 없다면 최상의 상속객체인 k3에서 찾게된다. k3에도 없기 때문에 그 다음 상속된 객체인 car에서 찾게되고 car에서 찾은 후에는 찾는 과정을 멈추고 4라고 반환하게 된다. 이런 과정을 Prototype Chain이라고 한다.

 

상속에 따라 메서드 사용이 달라질 수 있다.

위 예제를 가지고 왔다고 생각을 하고

for( p in k3_hev){ // for in을 통해 반복하여 객체 반환
  console.log(p);
}; 
/*moter
name
color
wheels
drive
*/

k3_hev > {moter: 'hev'}
Object.keys(k3_hev); > ['motor']
Object.values(k3_hev); > ['hev']

위 예제를 살펴보면 for in을 통해 객체를 순회했을 때에는 상속된 객체까지 순회하여 모든 키값을 반환했다.
반대로 keys,values를 통해 반환했을 때에는 k3_hev가 가지고 있는 객체 내에서만 반환했다.
이렇게 상황에 따라 사용할 방법이 달라질 수 있다.

 

만약 for in을 통해 반환하고 싶다면 hasOwnProperty를 사용하자.
앞서 언급한대로 hasOwnProperty는 객체 내에서만 탐색하기 때문에

/*for(p in k3_hev}{
console.log(p);
};
*/
for(p in k3_hev){
  if(k3_hev.hasOwnProperty(p)){
    console.log(p);
  };

이렇게 변경한다면 객체 내에 있는 값만 true를 반환하기 때문에 상속된 값이 반환되는걸 막을 수 있다.

생성자 함수를 이용한 상속

생성자 함수를 통해 만든 객체에도 당연히 상속을 할 수 있다.

const car = {
  wheels:4,
  drive(){
    console.log('drive');
  },};

const Kia = function(color) {
  this.color = color;
};

const k5 = new Kia('red');
const k3 = new Kia('white');

k5.__proto__ = car;
k3.__proto__ = car;

앞선 예제는 k3,k5객체를 직접 정의했다면 이번에는 생성자 함수를 통해 객체를 만들었다.
그런데 개발자들은 불편한 걸 그냥 보고있지 않는다.
위 코드에서 생성자 함수를 통해 만든 객체에 계속해서 상속을 해야하기 때문에 많아지면 더 불편하게 다가온다.
그래서 생성자 함수에 상속을 할 수 있는 기능이 있다.

const Kia = function(color) {
  this.color = color;
};

Kia.prototype.wheels = 4;
Kia.prototype.drive = function () {
  console.log('drive');
};


const k5 = new Kia('red');
const k3 = new Kia('white');

같은 기능을 하는 코드를 이렇게 간단하게 작성할 수 있다.
여기서 사용된 prototype은 생성자 함수 객체에 . 이후에 오는 프로퍼티를 상속한다. 즉 기존 코드에 k5.__proto__ = car; 부분을 적은 것이라 생각하면 된다.



생성자 함수를 통해 만들어진 객체는 생성자 함수의 인스턴스(instance)라고 불린다
그리고 이를 확인 할 수 있는 instanceof 연산자가 존재하며 값은 불린 값을 반환한다.

위 예제를 예로 들어
k5 instanceof Kia는 true를 반환한다.

생성자 함수의 인스턴스 객체에는 constructor이라는 프로퍼티가 존재한다.

k5.constructor === Kia > ture

객체.constructor은 그 객체에 생성자 함수를 뜻한다.



다시 위에 예제로 돌아가서 우리는 위 코드를 더 간단하게 줄 일 수 있다.

const Kia = function(color) {
  this.color = color;
};

Kia.prototype = {
  //constructor : Kia,
  wheels: 4,
  drive(){
  console.log('drive');
  },
};

const k5 = new Kia('red');
const k3 = new Kia('white');

이와 같은 방법으로 생성자 함수에 상속을 객체로 만들어 더 간단하게 작성 할 수 있다. ( 이 방법은 상속될 프로퍼티가 많으면 많을 수록 도움이 될 것이다.)
다만 이 방법을 사용하면 k5.constructor === Kia > false 를 반환한다.
그래서 우리는 이 방법을 사용안하거나, 주석처리가 된 부분처럼 직접 입력해 사용할 수 있다.

Comments