Prototype Chain

WooSeong·2021년 4월 9일
0

학습 노트

목록 보기
11/22
post-thumbnail

자바스크립트는 프로토타입 언어이다.

자바스크립트는 근본적으로 프로토타입 언어이다. 물론 발전을 거듭하여 멀티패러다임 언어로 거듭나긴 했지만, 세상에 처음 나왔을때는 그렇지 않았다. 시대가 흘러 프로그래밍 패러다임에서 객체 지향 프로그래밍이 대세가 되면서 자바스크립트에서도 객체 지향 프로그래밍을 할 필요성이 생겼다.

프로토타입 기반 언어에서 OOP를 구현하기 위한 과정중 나온 개념이 바로 프로토타입 체인(Prototype chain)이다. 프로토타입 체인을 통해 프로토타입 언어에서도 상속을 구현할 수 있게 되었다.

Prototype

그렇다면 프로토타입이 뭘까? 프로토타입은 선조라고 할수 있다. 나를 예로 들어보면, 나에게 있는 특징(주로 외견적인 특징)이나 행동들은 유전을 통해 나의 부모님 그리고 부모님의 부모님 거슬러 올라가 인류의 첫번째 조상에게서 물려 받은 것들이다. 나를 객체로 바꾸자면 객체의 속성이나 메소드는 상위 객체에게 물려 받은 것들로 구성되어 있다. 이때 상위 객체를 prototype object라 하는 것이다.

프로토타입은 객체가 메소드와 속성들을 상속 받기 위한 템플릿이다.

Prototype Chain

프로토타입 객체는 상위의 프로토타입 객체로 부터 메소드와 속성을 상속 받을 수 있고 상위의 프로토타입 객체도 그 상위의 프로토타입 객체로 부터 상속을 받을 수 있다. 이렇게 상속이 프로토타입을 거쳐 계속 내려오며 연결되는것이 바로 프로토타입 체인이다. 그리고 정확하게 prototype은 상속되는 속성과 메소드들이 정의되어 있는 객체의 생성자를 의미한다.

프로토타입 체인의 모습

프로토타입 체인의 개념은 이제 알았다. 그렇다면 자바스크립트에서는 어떻게 구현될까?

Pseudoclassical

우선 객체를 하나 정의하도록 하자. 이 객체는 앞으로 작성할 객체의 가장 위에 존재하는 원형 객체이다. 자바스크립트는 함수도 객체이다. 함수를 통해 객체를 만들어 보자.

//Human 이라는 객체를 정의, 이때 객체는 함수로 표현된다.
const Human = function(name) {
	//해당 객체의 속성은 name이 있다.
	this.name = name;
}
	
//해당 객체의 메소드는 eat이 있다.
Human.prototype.eat = function() {
	console.log('냠냠');
}

이제 또 다른 객체인 kim을 생성해 보도록 하자. 김씨는 사람이다. 따라서 김씨를 규정할때 처음부터 모든 것을 다 할 필요 없이 사람의 기본적인 속성과 행동에 김씨의 특징만 반영하면 될 것이다. 즉 kim객체는 Human객체를 프로토타입으로 하여 상속받으면 된다.

//Human에 kim 전달인자를 넣은 인스턴스를 생성하라는 의미
const kim = new Human('kim');

//kim은 정의되어 있는것이 아무것도 없지만 Human의 속성과 메소드를 상속받아 자신의 것처럼 사용할 수 있다.
kim.eat(); //'냠냠'

사람이 먹기만 하진 않을 것이다. 사람은 여러가지 활동을 한다. kim이 자기도 하고 노래를 부르게도 하고 싶다면 Human에 메소드를 추가해 주기만 하면 된다!

Human.prototype.sleep = function() {
	console.log('쿨쿨');
}
Human.prototype.sing = function() {
	console.log('라라라~');
}

kim.sleep(); //'쿨쿨'
kim.sing(); //'라라라~'

이번엔 park에게 직업을 줘보자! 먹고 자고 노래만 부르는건 배짱이다. 개발자라는 직업을 주기 위해선 Human에 개발자 속성과 메소드를 추가해야 할까? 모든 사람이 개발자는 아니다. 사람이 개발자를 포함하는 개념이고 개발자도 사람이기 때문에 개발자가 사람의 속성과 메소드를 상속받게 처리하면 될 것 같다. 개발자라는 새로운 객체를 만들어 보자!

const Developer = function(name, language) {
	Human.call(this, name);	
	this.language = language;
}

Developer.prototype.think = function() {
	console.log('생각하는중');
}
Developer.prototype.coding = function() {
	console.log('코딩하는중');
}

만들어진 developer를 park에게 상속시키면 개발자가 된다. 하지만 중요한건 사람이 아니게 된다... 이건 말이 안된다. developer가 human을 상속받고 park이 developer를 상속 받도록 해야 사람이자 개발자가 될 수 있다.

Developer 객체에서 Human 객체로 name 속성을 연결시켜 주기 위해 call을 사용하였다.

이제 여기서 프로토타입 체인이 나온다. 프로토타입 체인을 구현하기 위해 3가지 방법을 생각해 볼 수 있다.

  • Developer.prototype.__proto__에 직접 Human.prototype을 연결하는 방법

    → 해당 속성은 예전 스펙이 legacy 처럼 남아 있는 것이기 때문에 코드에서 직접적으로 접근하는 것은 피해야 한다!

  • Developer.prototype 에 Human.prototype을 재할당 하는 방법

    → 이렇게 할 경우 Developer의 인스턴스들은 Developer와의 연결이 완전히 사라지게 되고 Human과 직접적으로 연결되게 된다.

  • Object.create()를 사용하는 방법

    → 이 방법은 Human.prototype의 복사본을 Student.prototype에 연결하는 개념이다.

3가지 방법중 마지막 방법이 가장 권장된다고 말할수 있다.

Developer.prototype = Object.create(Human.prototype);

이 방법은 Human.prototype의 복사본을 Developer.prototype에 연결시킨다. 이를 다르게 말하자면 constructor 또한 Human의 constructor가 연결된다. 따라서 한가지 작업을 더 해줘야 한다. Developer의 constructor는 Developer에 연결되어야 하니까.

Developer.prototype.constructor = Develper;

const park = new Developer('park', 'javascript');

park.coding() // '코딩하는중'
park.eat() // '냠냠'
park.think() // '생각하는중'

드디어 우리 박씨는 개발자로써 코딩도 하고 생각도 하며 인간으로써 먹는 것도 할수 있게 되었다!

ES6 (class, constructor, extends, super)

프로토타입 기반 언어인 자바스크립트 에서도 프로토타입 체인을 이용하여 객체 지향 프로그래밍을 할수 있게 되었지만, 상당히 복잡한 작업을 수반해야 하는 것을 알 수 있다. 코드의 가독성을 높이고 좀더 sementic 하게 표현할 수 있도록 ES6에 와서 새로운 문법이 도입되었다. pseudoclassical 하게 쓰여진 코드를 ES6 문법으로 고쳐써 보자!

class Human {
	constructor(name) {
		this.name = name;
	}
	eat() {console.log('냠냠')}
	sleep() {console.log('쿨쿨')}
	sing() {console.log('라라라~')}
}

const kim = new Human('kim')

class Developer extends Human{
	constructor(name, language) {
		super(name);
		this.language = language;
	}
	coding() {console.log('코딩하는중')}
	think() {console.log('생각하는중')}
}

const park = new Developer('park', 'javascript');

ES6 이전 구현에 비해 훨씬 간단해지고 가독성도 좋아졌다.

  • class는 기존의 함수형태의 객체 표현을 대체하는 키워드이다.
    • 그렇기 때문에 class는 사실 '특별한 함수'다! class 또한 class 표현식으로 정의 할수 있다.
    • class 선언은 기존의 함수 선언과 비교하여 호이스팅이 일어나지 않는다.(let으로 선언된 class 표현식 에서 조차 호이스팅이 일어나지 않는다)
    • class 컨텍스트 내부는 항상 'use strict'(엄격 모드)로 동작한다. 따라서 this 사용에 유의해야 한다.(this는 더이상 자동 바인딩 되지 않는다!)
  • constructor는 명시적으로 class의 constructor를 의미한다.
    • ES6 이전의 constructor는 함수의 매개변수로 표현 되었으며 다소 모호한 표현이었다.
    • 부모의 constructor와 동일하다면 constructor를 생략할 수 있다. extends가 부모의 생성자또한 참조하기 때문이다.
  • 상속은 extends 라는 키워드로 압축되었다
    • extends는 2가지 역할을 동시에 수행한다.
      • Developer.prototype = Object.create(Human.prototype);
      • Developer.prototype.constructor = Developer;
    • 기존 클래스의 속성이나 메소드를 추가(확장)한다는 의미로서 sementic하게 변하였다.
  • 부모 class로 this를 연결시켜 주기 위한 방법이 super로 간단해졌다.
    • Human.call(this, name);이 super(name);으로 바뀌었다.
    • constructor에서는 super 키워드 하나만 사용되거나 this 키워드가 사용되기 전에 호출되어야만 한다.
profile
성장하는 개발자를 꿈꿉니다

0개의 댓글