loading

자바스크립트(javascript) - 프로토타입 상속, 클래스 상속, 개념 잡기

 

 

 

자바스크립트는 객체지향의(c++, JAVA) 언어와 다르게 프로토타입 기반으로 하는 언어이다. 

그렇기 때문에 다른 언어와 차이점 있다.

 

프로토타입이라고 함은 간단하게 말해서 모든 객체가 연결이 되었다고 표현을 하면 될 것 같다. 

일반적인 객체지향의 언어에서는 볼 수 없는 환경이다. 

 

이 프로토타입 기반의 클래스 상속을 알기 전 나는 얼마만큼 자바스크립트의 프로토타입에 대해 알고있는지 판단해봐야 한다.

 

자바스크립트의 프로토타입의 개념을 알지 못하면프로토타입 기반의 클래스 상속 개념을 이해하기 어려울 것이다.

프로토타입 링크글 ☜

 

위 링크를 걸어놨으니 프로타타입을 잘 모르겠다 하면 위 링크글을 참조하여 읽어보는 것도 좋다.

 

자바스크립트에서는 타 객체지향 언어인 C++, JAVA의 클래스 상속과 같은 기술을 구현하기 위해서 

수많은 노력을 해왔지만 언어의 태생이 프로토타입의 기반을 두고 있어서 

타 객체지향 언어인 C++, JAVA와 같은 클래스 구현에 있어서 한계점이 나타날 수밖에 없었다.

 

You don't Know Js의 저자인 카일 심슨은 프로토 기반의 언어인 

자바스크립트에서의 상속 구현에 있어서 자신만의 방법을 제시하였는데 이 방법을

OLOO(Objects Linked to Other Objects) 스타일의 코딩 법을 제시하였다. 

 

자바스크립트 ES5 에서 프로토타입의 상속이 무엇인지 개념을 확인 한 후에

카일심슨이 구현한 방법을 알아보고 기존과 어떤 점이 더 나은지 확인 후

ES6 에서 Class 문법이 도입 된 이후의 나은점이 어떤지 확인해보자.

 

자바스크립트에서의 클래스 구현 방법 3가지를 알아보자.

  1. ES5 프로토타입 스타일로 구현
  2. OLOO 스타일로 구현
  3. ES6 문법의 Class로 구현

 

이렇게 3가지 클래스 구현 방법을 알아볼테니 만약 클래스 형태로 개발을 구현하려고 한다면

자신의 편한것을 선택해 위 3가지중 한가지 방법으로 구현해서 개발을 해도 좋다.

 

▶ 프로토타입기반

이전 글에서 프로토타입의 메커니즘에 대해 설명해놨는데 프로토타입이 자체를 모른다 하면

이전 글을 먼저 읽고 이 글을 읽는 것을 추천한다.

 

프로토타입 링크

 

프로토타입이라 함은 말 그대로 모든 객체가 [[Prototype]]이라는 내부 속성을 가진다.

즉, 모든 객체가 연결이 되어 있기 때문에 내부 속성을 이용한 코딩 법에 의해

클래스를 비슷하게 구현하게 할 수 있고, 

 

또한 잘 사용한다면 불필요한 코딩의 줄 수도 획기적으로 줄일 수 있기 때문에

자바스크립트 개발자들은 프로토타입이라는 내부 속성에 대해 자세히 알 필요가 있다.

 

▶ ES5에서의 클래스 구현

ES5에서 클래스 구현을 어떻게 진행했는지 간단한 예제를 통해서 알아보자.

예제)

 

function saveName(fName, lName, age){
	//console.log('부모', this);
    this.firstName = fName;
    this.lastName = lName;
    this.age = age;
}

function name(fName, lName, age){
	//console.log('자식', this);
    saveName.call(this, fName, lName);
}

name.prototype = Object.create(saveName.prototype);
name.prototype.constructor = name;



saveName.prototype.getFirstName = function(){
    return this.firstName;
}

name.prototype.fullName = function(){
    alert(this.getFirstName() + ' ' + this.lastName + ' ' + this.age +'세');
};


var personA = new name('박', '영웅', '18');
personA.fullName(); // 박 영웅 18세

 

코드 내용

saveName이라는 함수는 부모 클래스라고 생각하고,

name이라는 함수는 자식 클래스라 생각하자.

 

부모로부터 상속을 받고 new라는 키워드로 객체를 personA 생성하여 fullName이라는 함수를 실행시켜서
부모로부터 물려받은 함수 및 객체에 접근할 수 있는 내용이다.

 

 

소스 분석

saveName 함수는 부모 클래스
name 함수는 자식 클래스


name.prototype = Object.create(saveName.prototype); 이 부분이 상속하는 부분이다.
이 Object.create의 기능은 새로운 개체를 만들어서 자식의 [[Prototype]]에 연결시킨다. 

 

여기서 중요한데, 자바스크립트의 상속하는 과정에서 단점이 드러나는 부분이 바로 이점이다.
이 Object.create는 기존에 들어있는 개체를 버리고 새로운 개체로 대체되는 거라 기존 개체의 정보가

 남아있지가 않다. 이 부분부터 하나하나씩 확인해 보자. 

 

어떻게 상속이 되는지...

 

1. 상속하는 부분을 전부 주석 처리하고 consosle.log로 부모/자식에 해당하는 함수의 prototye을 확인해보자.

이전 글에서도 설명했듯이 constructor는 본인 자신에 해당하는 함수이다. 즉, 자기 자신이라는 것이다. 

 

//name.prototype = Object.create(saveName.prototype); // 이 부분 주석처리
//name.prototype.constructor = name; // 이 부분 주석처리

console.log(saveName.prototype, name.prototype);

 

이렇게 console.log로 확인해보자.  

 

saveName / name 의 prototype 콘솔로그 결과화면

 

보이는가? 위 출력 화면 중 녹색 박스 되어 있는 부분 주목하자.

saveName(부모)/ name(자식) 클래스의 함수들과 비교를 하면 같다는 걸 알 수 있다.

 


2. Object.create를 사용해서 상속 코드 이후 console.log로 확인해보자. 

 

name.prototype = Object.create(saveName.prototype); // 이 부분 주석을 풀고

console.log(saveName.prototype, name.prototype); // 출력확인

 

이렇게 console.log 확인해보자.

 

상속 후 saveName / name 의 prototype 콘솔로그 결과화면

 

Object.create 결과 후 어떤가? 

자식의 constructor 기존 게 사라졌다. 그리고 자식의 [[Prototype]] 열어보자.

 

Object.create 실행 결과화면


상속을 받은 것처럼 자식의 [[Prototype]] 안에 들어 있다. 즉, 연결이 되었다. 



3. 하지만 우린 아까 얘기했던 것처럼 Object.create를 사용할 때 기존 개체가 없어지고 새롭게 바뀐다고 했다. 

자세히 생각해보자.


자식 함수인 name.prototype 는 정체성을 잃어버렸다. constructor는 원래가 자기 자신이 가지고 있어야 하는데,

기본 타 객체지향 프로그램 언어에서나 자바스크립트 ES6 에서는 클래스를 구현할 때 인스턴스 객체를 생성할 때

초기화하는 가장 기초적이면서 기본적인 것인데 이것을 생성자 메서드라 부르는데,

 

자바스크립트에선 Object.create를 사용해서 상속을 받게 되면 기존 자기가 가지고 있는 constructor가 

없어지기 때문에 name.prototype.constructor = name; 이 부분을 이용해서 원래 자신의 constructor를 

다시 지정해 주는 것이다.

 

그래서 아래 코드와 같이 주석을 풀고

 

name.prototype.constructor = name; // 이 부분을 주석을 푼다.

console.log(saveName.prototype, name.prototype); // 출력확인

 

콘솔 로그 화면에 무엇이 나오는지 확인해보자.

 

결과화면

 

보이는가? 

name.prototype.constructor = name; 이 명령어로 인해 name이라는 자식은 이제 정체성을 찾았다.

결과 화면의 녹색 박스를 봐보자 constructor 자기 자신을 가리키고 있다.

 

이제 부모 함수(클래스)에 getFirstName 함수를 정의한다.
자식 함수(클래스)에 fullName 함수를 정의한다.

personA 변수에 자식 함수(클래스) name 으로 객체를 생성한 후에
personA.fullName(); // 박 영웅 18세

라고 출력을 한다.

 

자 우리는 new 키워드로 객체를 생성할 때 매개변수에 박, 영웅, 18이라는 매개변수가 어떻게 자식과 부모가

공유를 하고 있는지 초점을 맞춰야 한다.


위 전체 코드에서 각 부모 / 자식 함수에

console.log('부모', this); // 이 부분 주석을 풀어서 확인
console.log('자식', this); // 이 부분 주석을 풀어서 확인

 

이렇게 주석을 풀어서 콘솔 로그 화면을 확인해보면

 

this 확인 결과화면

 

this가 부모 / 자식 각 쓰인 게 같다는 걸 알 수 있다.

 

여기에서 this를 이해 못 하면 아무것도 이해 못 하니

this에 대한 기본, 암시적, 명시적, new 바인딩의 4가지를 알고 넘어가야 한다.


이전 글을 참조해서 꼭 알고 넘어가자

아래 1~ 4번 링크 걸어놨으니 참조 바람

 

  1. this 기본 바인딩
  2. this 암시적 바인딩
  3. this 명시적 바인딩
  4. this new 바인딩

 

이렇게 부모/자식 간에 프로퍼티(속성)들도 공유가 되어야 하기 때문에 name의 자식 함수에서 function함수의 prototype에 연결되어 있는 call 메서드를 통해서 부모를 호출하고 있다.
그래서 결론적으로 자식의 this가 부모 함수에서의 this와 같게 되는 것이다. 

 

객체 personA는 부모의 메서드인 getFirstName 이란 함수를 어떻게 실행했을까?
personA 객체는 name으로 만든 자식 함수의 객체이다. 

 

그러므로 자기 자신이 가지고 있는 fullName이라는 것을 호출할 수 있었고 name 자식 함수(클래스)는

부모로부터 상속을 받았기 때문에

즉, [[Prototype]]안에 연결되어 있어서 getFirstName()을 실행할 수 있었다.

이렇게 Object.create 가지고 상속?

같은 개념으로다가 생각해서 자바스크립트는 구현을 할 수가 있다. 하지만..

이것이 진정하게 타 객체지향 언어처럼 완벽한 클래스 상속 개념이 아니기 때문에 단점은 존재한다. 

 

 

반응형

 

▶ OLOO에서의 클래스 구현

 

이 OLOO 스타일의 코드로 구현하면 기본 ES5에서 클래스 구현하는 방법보다 좀 더 간단해진다. 

어떤 부분이 더 간단해지는지 또 기존 방법보다 어떤 것이 편리한지 확인해보자.

 

예제)

 

var saveName = {
    init:function(fName, lName, age){
        this.firstName = fName;
        this.lastName = lName;
        this.age = age;
    },
    getFirstName:function(){
        return this.firstName;
    }
}



var name = Object.create(saveName);

name.fullName = function(){
    console.log(this.getFirstName() + ' ' + this.lastName + ' ' + this.age +'세');
}

var personA = Object.create(name);
personA.init('박', '영웅', '18');
personA.fullName();

 

기능은 기존 ES5 프로토타입으로 구현한 클래스 기능과 같은 기능을 한다.


무엇인가가 좀 더 깔끔해졌다. 그리고 기존 꺼와 비교해보면 사용하지 않은 메서드도 보인다.
OLOO의 코드 스타일이 기존 꺼와 어떤 점이 더 나아 보이는가?

  • 첫 번째로 코드가 줄었다.
  • 헷갈리지 않게 코드가 직관적으로 바뀌었다.
  • 객체.prototype, 함수.call 등등 이런 것들을 직접적으로 보이지 않는 내부 속성에 있는 메서드들을 사용하지 않았다. 즉, 높은 수준의 자바스크립트의 지식을 요구하지 않았다. prototype 이라느니.. function 내부 속성인 call 메서드라느니.. 등등..


OLOO 방식은 위 기존 ES5에서 프로토타입으로 구현한 클래스보다 간단하면서도 이해가 쉽다.
즉, Object.create를 통해서 자식의 [[Prototype]] 안에 

새로운 개채를 만들어서 연결 시는 것만 알면 되기 때문이다.


그래서 이걸 행동 위임이라 부른다. 

즉, 새로운 개채를 만들어서 네가 없는걸 [[Prototype]]에 연결해서 위임하기 때문이다.

OLOO(Objects Linked to Other Objects - 다른 개체에 연결된 개체) 다른 개체에 연결된 개체.

왜 이 이름인지 이해가 가는가? 

 

 

▶ES6에서 지원하는 Class로 구현

ES6 에서부터 Class라는 문법이 추가가 되었고,

Class를 지원하는 코드의 형태도 다른 객체지향 언어와 비슷하게 되었다.

예제를 통해서 어떻게 구현이 되는지 확인해보자.

 

예제)

 

class saveName {
  constructor(fName, lName, age){
    this.firstName = fName;
    this.lastName = lName;
    this.age = age;
  }
  
  getFirstName() {
    return this.firstName;
  }
}



class name extends saveName {
  constructor(fName, lName, age){
    super(fName, lName, age);
  }
  
  fullName() {
    console.log(this.getFirstName() + ' ' + this.lastName + ' ' + this.age +'세');
  }
}

const personA = new name('박', '영웅', '18');
personA.fullName(); // 박 영웅 18세

 

자 어떤가? 
눈에 확 들어고, 이런 코드가 훨씬 더 유지보수에 적합하지 않은가?

하지만 자바스크립트에선 이렇게 class 문법이 추가가 되어있어도, 

코드의 실행 절차는 기존 ES5에서 프로토 기반으로 해석되어 돌아간다고 생각하면 된다.

아무튼 우리는 이렇게 자바스크립트에서 상속되는 방법을 알았다.
기존 ES5에서 상속 방법은 코드가 길어지면 실수가 많아지고, 유지보수에도 힘들 것이다.

 

기존 방법이 편하다면 OLOO 방법으로 구현을 하면 될 것이고, 아니면 ES6의 Class 문법을 지원하니

이걸 사용함으로써 구현하는 게 바람직할 것 같다.

반응형

+ Recent posts