본문 바로가기
JavaScript | 자바스크립트

OOP - 객체지향 / 상속

by Pig_CoLa 2020. 7. 29.
SMALL

객체지향이란

Object Oriented Programming 의 약자로 하나의 페러다임이다.

간단하게...

각각의 객체를 생성하는 것은 클래스이며 생성된 객체를 인스턴스라고 부른다.

인스턴스는 각각의 고유한 속성을 지니는것이 가능하다.

또, 새로운 클래스를 만들 때 이전에 만든 클래스를 상속 받을 수 있다

왜 써야 하나?

객체 지향을 사용하는 이유는 실생활의 무언가를 가장 잘 표현할 수 있기 때문이다.

감이 잡히지 않는다면 class instance 또는 Object Oriented Programming

에 대해서 real-life example 키워드와 함께 검색해 보는것을 권장한다.

예제

  • 금형 - 클래스
    • 빵틀 - 금형 클래스를 상속받는다.
      • 붕어빵 틀 - 빵틀 클래스를 상속받는다. (모양 이라는 속성값은 붕어빵모양 이다.)
        • 그냥 붕어빵 - 붕어빵 틀의 인스턴스이다. (앙꼬 속성에 대한 값은 팥 이다)
        • 슈크림 붕어빵 - 붕어빵 틀의 인스턴스이다. (앙꼬 속성에 대한 값은 슈크림 이다)
      • 풀빵 틀 - 빵틀 클래스를 상속받는다. (모양 이라는 속성값은 국화모양 이다.)
        • 국화빵 - 풀빵 틀의 인스턴스이다. (앙꼬 속성에 대한 값은 팥 이다)
        • 밤빵 - 풀빵 틀의 인스턴스이다. (앙꼬 속성에 대한 값은 밤 이다)

JavaScript - OOP

JavaScript를 객체지향언어라고 말하는 사람도 있는 반면

아니라고 하는 사람도 존재한다.

 

그 이유는 JavaScript가 ES6에서 class키워드를 도입했음에도 불구하고

여전히 prototype기반 언어이기 때문이다.

 

타 언어에서 객체지향언어라 함은 대부분 class기반이기에

JavaScript에서 만큼은 상속, 메서드 정의 등등 여러 가지 차이점을 보인다.

(이를 해결하기 위해 ES6에서 class키워드를 도입하게 된것이다.
그렇다고 class기반 언어가 된것은 아니라는 점을 유의해야 한다.)

JavaScript에서 OOP 구현하기

opp를 구현한다는 뜻은 다음과 같다.

  1. class를 만들고 이를 통하여 instance를 생성할수 있어야 한다.
  2. class에 메서드를 만들고 이를 instance에서 사용할 수 있어야 한다.
  3. 다른 class를 만들때에 기존 class에 대하여 상속 받을 수 있어야 한다.
  4. 상속 받은 class의 instance에서 여전히 상속대상 class의 메서드를 사용할 수 있어야 한다.

ES5 문법으로 OOP 구현하기

class키워드가 도입되기 전이다.

대부분 ES6문법으로 바꾸고 있기때문에 마주칠 일은 적다고 할 수 있지만

개념에 대해서 확실히 알고 있어야 하고, 간혹 여러가지 이유로 ES5문법을 써야만 할때도 존재한다.

class 정의

function Person(name) {
    this.name = name;
}

함수를 정의하는것과 같은 방식으로 정의한다.

다만 차이를 두기 위하여 class에선 함수이름의 첫 글자는 무조건 대문자로 작성해 주는 것이 암묵적인 룰이다.

method 정의

function Person(name) {
    this.name = name;
}
// class의 메서드를 정의하기 전에 클래스가 정의되어있어야 한다.

Person.prototype.sleep = function() {
    return `${this.name}: Zzz...`;
}
// class자체에 method를 정의하는 것이 아닌 prototype에 정의해야 한다는 것을 유의할 것.

instance 생성

단순히 new키워드와 함께 함수(class)를 호출한다. 이때 전달인자가 필요하다면 일반적인 방법으로 넣어주면 된다.

function Person(name) {
    this.name = name;
}

Person.prototype.sleep = function() {
    return `${this.name}: Zzz...`;
}

// Person에 대한 instance생성
let a = new Person("Hwang");

// instance에서 메서드호출
a.sleep() // "Hwang: Zzz..."

상속

가장 중요한 개념이다.

상속받은 class의 instance는 부모 class가 정의한 속성을 전부 가지고 있어야 하며 부모 class의 메서드사용이 가능해야 한다.

또한 스스로 정의된 속성과 메서드가 겹치는 경우 그것을 update한다.

// 부모class가 될 Person정의
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sleep = function() {
    return `${this.name}: Zzz...`;
}

// Person을 상속할 class 정의
function Student(name, age, schoolName) {
    Person.call(this, name, age) // 부모class를 호출해주어야 한다.
    // 꼭 call, apply와 같이 this를 바인드하여 호출해야 한다는 것에 유의할것.
    this.schoolName = schoolName;
}

// 메서드 정의는 상속이 끝난후에 이루어져야 한다. (상속과정중에 prototype이 변경되기 때문)

// 상속과정 시작 --- 중요!
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student; // constructor관계가 깨지므로 다시 설정해줘야함.
// 상속과정 끝

Student.prototype.greeting = function () { // 상속받은 class에도 method 정의가 가능하다.
    return `hi, my name is ${this.name}.
i'm ${this.age}years old, and I'm belong to ${this.schoolName}`;
}

// Student의 instance생성
let a = new Student("Hwang", 10, "한국초등학교");
console.log(a);// Student {name: "Hwang", age: 10, schoolName: "한국초등학교"}

a.greeting(); // 새로 정의한 메서드 사용이 가능하다.

// "hi, my name is Hwang.
// i'm 10years old, and I'm belong to 한국초등학교"

a.sleep(); // Person의 메서드 사용이 가능하다.
// "Hwang: Zzz..."

 

A를 B에 상속하기 위해서 B.prototype = A.prototype이 아닌
B.prototype = Object.create(A.prototype)을 해아하는것에 유의해야 한다.

만일 A.prototype자체를 넣어주게 된다면 B에 만들어준 method가 A에도 존재하는 것을 보게된다.

참고하면 좋을 자료 - Object.create에 대해

이와같은 패턴을 prototype-chain이라고 한다.

ES6 문법으로 OOP 구현하기

class, extends등 새로운 키워드가 도입되었다.

그럼에도 불구하고 새 키워드가 위 과정을 대체할 뿐, prototype에 대한 chaining이 없는 것은 아니다.

여전히 prototype기반 언어임에는 변함이 없다는것에 유의.

class 정의

// 방법1
class Person {
    constructor(name) {
        this.name = name;
    }
}

///////////////////////////////////////////////

// 방법2
const Person = class {
    constructor(name) {
        this.name = name;
    }
}

class키워드를 이용하여 정의한다.

class역시 방법2와 같이 표현식을 사용할 수 있다.

method 정의

class Person {
    constructor(name) {
        this.name = name;
    }

    // ES6문법 에서는 class를 정의할때 메서드를 정의할 수 있다.
    sleep = function() {
        return `${this.name}: Zzz...`;
    }
}

// 물론 여전히 prototype이 존재하기에 class정의 후에도 얼마든지 정의해 줄수 있다.
Person.prototype.deepSleep = function () {
    return 'ZZZZZZZZzzzzzzzzzzzzzz...'
}

instance 생성

new키워드를 사용하여 호출한다.

(class 키워드를 사용해 만든 class는 new키워드로만 호출이 가능하다)

let a = new Person("Hwang"); // 참조오류

class Person {
    constructor(name) {
        this.name = name;
    }

    sleep = function() {
        return `${this.name}: Zzz...`;
    }
}

// Person에 대한 instance생성
let a = new Person("Hwang");

// instance에서 메서드호출
a.sleep() // "Hwang: Zzz..."

function키워드와는 다르게 hoisting이 일어나지 않는다.

상속

extends키워드를 이용한다.

// 부모class가 될 Person정의
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    sleep() {
        return `${this.name}: Zzz...`;
    }
}

// Person을 상속할 class 정의
class Student extends Person {
    constructor(name, age, schoolName) {
        super(name, age) // super키워드를 통하여 부모 class를 호출한다.
        // 상속시 this사용은 super키워드 아래에서만 가능하다.
        this.schoolName = schoolName;
    }

    greeting() { // 상속받은 class를 선언할때에도 method 정의가 가능하다.
        return `hi, my name is ${this.name}.
i'm ${this.age}years old, and I'm belong to ${this.schoolName}`;
    }
}

// Student의 instance생성
let a = new Student("Hwang", 10, "한국초등학교");
console.log(a);// Student {name: "Hwang", age: 10, schoolName: "한국초등학교"}

a.greeting(); // 새로 정의한 메서드 사용이 가능하다.

// "hi, my name is Hwang.
// i'm 10years old, and I'm belong to 한국초등학교"

a.sleep(); // Person의 메서드 사용이 가능하다.
// "Hwang: Zzz..."

상속 받은 이후에 class의 prototype을 통한 메서드 추가역시 당연히 가능하다.

 

ES5문법과 ES6문법의 class특징 차이점

  1. ES5문법은 function키워드를 사용하기에 hoisting이 일어나
    script 최하단에서 정의되어 있더라도 instance생성이 가능하다.
    하지만 ES6문법 class 키워드를 사용할경우 그렇지 않다.
  2. ES5문법을 사용여 function키워드가 사용된 class를 상속시
    call, apply등으로 호출하는데 반면
    ES6문법인 class키워드를 사용시 new키워드 없이 호출이 불가능하다.
    그래서 상속시 super키워드를 이용해야 한다.
  3. ES6문법을 사용한다면 상속 여부와 관계없이
    method 정의가 class선언 동시에 가능하다.
    물론 후에 prototype을 통한 method정의 역시 가능하다.

JavaScript에서 class다중상속은 불가능하다.

prototype기반 언어이기에 '원칙'과 '이론'상 불가능하게 구성되어있다.

그럼에도 불구하고 여러가지 편법을 사용하여 다중상속이 가능하다.

이는 다음장에서 다루겠다.

다중상속이 뭔가요 - 훑어보기

  • A클래스

    • A를 상속받은 B클래스

      • B를 상속받은 C클래스

  • D클래스

    • D를 상속받은 E클래스

      • E를 상속받은 F클래스

  • C클래스와 F클래스를 모두 상속받은 G클래스

LIST

'JavaScript | 자바스크립트' 카테고리의 다른 글

Promise / async / await  (0) 2020.08.17
JavaScript - class 다중상속  (0) 2020.07.29
복잡도 - 시간복잡도  (0) 2020.07.19
재귀호출  (0) 2020.07.18
함수의 메서드 - call, apply, bind  (2) 2020.07.17

댓글