[JS] 7.1. 상속

작성:    

업데이트:

카테고리:

태그: , ,

참고 자료

boostcourse 생활코딩: 자바스크립트의 시작


상속(Inheritance)

  • class에 필요한 메서드가 있는데 추가할 수 없는 경우(다른 사람의 코드, 라이브러리 등)
  • class 자체에 추가하면서 무겁게 되길 원치 않는 경우

  • 모든 요소를 받을 클래스를 extends class명 을 통해서 부모 클래스로 두고 상속
class Person {
  constructor(name, first, second) {
    this.name = name;
    this.first = first;
    this.second = second;
  }

  sum() {
    return "class method > " + this.name + " : " + (this.first + this.second);
  }
}

class PersonPlus extends Person {
  avg() {
    return (this.first + this.second) / 2;
  }
}

var kim = new PersonPlus("kim", 10, 20);
console.log("kim.sum()", kim.sum());
console.log("kim.avg()", kim.avg());
  • avg() 메서드를 Person 클래스 자체에 넣지 않고, Person 클래스를 부모 클래스로 하여 상속받는 PersonPlus 클래스에 메서드를 새로 만들었다.
  • 부모 클래스의 요소인 name, first, second 등을 모두 받아온다.
  • 부모 클래스의 메서드도 사용 가능하고 자식 클래스의 메서드도 사용 가능하다.
  • 부모 클래스에 이미 있는 메서드가 마음에 들지 않는다면 자식 클래스에서 바꾸어도 된다.


super

  • 부모 클래스의 요소를 불러 가져오고, 부모 클래스에서 하지 못하는 것들을 자식에서 할 수 있도록 하는 것
class Person {
  constructor(name, first, second) {
    this.name = name;
    this.first = first;
    this.second = second;
  }

  sum() {
    return this.first + this.second;
  }
}

class PersonPlus extends Person {
  constructor(name, first, second, third) {
    super(name, first, second);
    this.third = third; // 자식 클래스에서만 추가되는 것
  }

  sum() {
    return super.sum() + this.third;
  }

  avg() {
    return (super.sum() + this.third) / 3;
  }
}

var kim = new PersonPlus("kim", 10, 20, 30);
console.log("kim.sum()", kim.sum());
console.log("kim.avg()", kim.avg());
  • super(param1, param2,...) : 부모 클래스의 생성자 호출, 자식 클래스에 super()로 가져올 수 있다.
  • 생성자 vs 생성자 이므로 자식 생성자 이전에 부모 생성자를 가져올 수 있다.
  • super.~~~ : 부모 클래스의 메서드 호출
  • super을 이용해 간단하게 상속 클래스에서 수정 가능


Object Inheritance

  • 일반적인 프로그래밍 언어에서 상속은 클래스끼리만 되고 인스턴스끼리는 상속이 되지 않는다.
  • 자바스크립트는 너무 유연한 언어라 super object로부터 sub object가 기능을 상속받을 수 있다.
  • 심지어 super object를 상속하고 있다가 다른 객체에게 상속받을 필요가 있다면 prototype link만 다른 객체로 바꿔주면 된다.
  • 이 prototype link는 별도의 지정이 없으면 super object와 연결되어 있는 것.
  • super와 상관 없이 prototype link로 연결되어 있는 객체를 prototype object라고 부른다.


__proto__

  • class를 사용하지 않고 __proto__를 사용해 상속한다.
  • 클래스와 클래스가 아닌, 객체가 객체를 상속하도록 하는 것이다.


var superObj = { superVal: "super" };
var subObj = { subVal: "sub" };
subObj.__proto__ = superObj;

console.log("subObj.subVal =>", subObj.subVal);
console.log("subObj.superVal =>", subObj.superVal);

// subObj.subVal => sub
// subObj.superVal => super
  • subObj에는 없는 superVal key를 이용해 super를 출력한다.
  • subObj.__proto__ = superObj;를 통해 인스턴스 상속을 한 것!
  • 우선 subObj에 superVal을 찾아보고 인스턴스 내에 이게 없으면 __proto__ 연결한 인스턴스에 있는지 확인


subObj.superVal = "sub";
console.log("superObj.superVal =>", superObj.superVal);

// superObj.superVal => super
  • subObj의 superVal 프로퍼티를 바꾸더라도, 객체의 proto의 프로퍼티가 바뀌지는 않는다.


  • prototype
    • constructor 안이 아닌 밖에서 Method를 지정할 수 있다.
    • 모든 객체들이 프로토타입을 함께 쓸 수 있게 한다.
  • __proto__ : 객체가 객체를 상속하도록 한다.


Object.create()

  • 객체가 객체를 상속받기 위해서 위에서는 __proto__를 사용하였다.
// __proto__ 사용

var superObj = { superVal: "super" };
var subObj = { subVal: "sub" };
subObj.__proto__ = superObj;


그런데 Object.create()를 이용해 부모 객체로부터 자식 객체를 만들 수 있다.

// Object.create() 사용

var superObj = { superVal: "super" };
var subObj = Object.create(superObj);
subObj.subcal = "sub";


call

  • 모든 함수는 call이라는 메서드를 가진다.
  • 모든 함수는 객체이기 때문
  • 객체와 연결해 객체의 속성을 이용해 함수 동작을 할 수 있다. (this가 객체가 됨)
  • 일시적으로 객체를 연결해 함수를 호출할 때 객체의 속성을 사용한다.
var kim = {
  name: "kim",
  first: 10,
  second: 20,
};

var lee = {
  name: "lee",
  first: 10,
  second: 10,
};

// 어느 객체에 속하지 않고 외부에 있는 함수
function sum() {
  return this.first + this.second;
}

// sum이라고 하는 객체를 실행한다는 의미( = sum(); )
sum.call(kim);

이렇게 call() 내부에 인자로 kim을 주게 되면, this는 kim이 되는 것이다.


call의 사용

// sum이라고 하는 객체를 실행한다는 의미( = sum(); )
console.log("sum.call(kim)", sum.call(kim));
console.log("sum.call(lee)", sum.call(lee));

// sum.call(kim) 30
// sum.call(lee) 20


parameter가 있는 경우

sum() 함수가 sum(prefix)처럼 인자를 가지고 어떤 객체의 프로퍼티를 사용한다고 가정해보자.

function sum(prefix) {
  return prefix + (this.first + this.second);
}

// sum이라고 하는 객체를 실행한다는 의미( = sum(); )
console.log("sum.call(kim)", sum.call(kim, " => "));
console.log("sum.call(lee)", sum.call(lee, " : "));

// sum.call(kim)  => 30
// sum.call(lee)  : 20

위에처럼 함수가 어떤 객체를 참조하는지 먼저 괄호에 쓰고, 뒤에 parameter를 추가한다.


bind

  • call()은 함수에 사용할 객체를 매번 지정해줘야 한다.
  • bind()는 함수가 인자를 참조하여 this로 고정하는 새로운 함수를 만드는 것
// 내부적으로 this를 kim으로 하는 새로운 함수 생성
var kimSum = sum.bind(kim, "-> ");
console.log("kimSum()", kimSum());

// kimSum() -> 30

kimSum()은 sum의 this를 kim으로 영구적으로 고정한 아예 새로운 함수이다.


prototype vs __proto__

  • 함수는 객체여서 메서드를 가질 수 있다.
  • 함수 객체가 만들어지면 prototype 객체도 같이 만들어진다.
  • 함수 객체와 prototype 객체는 연관이 되어있다.
    • 함수의 prototype 프로퍼티는 prototype 객체를 가리킴
    • prototype 객체의 constructor 프로퍼티는 함수 객체를 가리킴
    • 즉 서로 상호참조를 하고 있는 관계
function Person(name, first, second) {
  this.name = name;
  this.first = first;
  this.second = second;
}

// Person 함수 객체의 프로토타입 객체에 sum 함수를 생성 및 정의
Person.prototype.sum = function () {};

// 객체 kim 생성
var kim = new Person("kim", 10, 20);
  • 객체 kim을 생성하면 parameter로 전달해준 값이 내부에서 정의됨
  • __proto__ 프로퍼티가 kim 객체 내부에 생김
  • 이는 인스턴스를 만든 클래스의 prototype 프로퍼티를 가리킨다.
  • 결국 Person’s prototype 객체를 가리키는 것

  • 인스턴스 외부에서 인스턴스의 프로퍼티를 검색(kim.name)할 때

    • 인스턴스 내부에 해당 프로퍼티가 있으면 반환
    • 없으면 __proto__가 가리키는, 즉 Person’s prototype 객체 내에 있는지 확인
  • kim.sum()은 kim 인스턴스 내에 없다.
  • __proto__인 Person’s prototype 객체의 sum() 메서드를 불러온다.


번외: debugger

  • JS 코드에 debugger;를 입력하고 브라우저에 렌더링하면, 해당 부분까지 코드를 진행한 뒤 일시정지된다.
  • 크롬 브라우저의 개발자도구에서 객체의 상태를 자세히 확인할 수 있다.
  • [Sources - Watch]에 객체명을 검색해 상세 내용을 확인하면 된다.

댓글남기기