티스토리 뷰

1) 생성자패턴

들어가기 전에

  • 자바스크립트에서 함수만으로 나누기 보다는, 어떠한 집합개념으로 모듈화를 할 필요가 있습니다.
  • 객체지향 언어에서는 클래스라고 하는 것이 그것입니다.
  • 자바스크립트도 객체 형태로 비슷한 속성과 행위를 묶어서 표현할 수 있는데, 좀 더 객체지향적인 방법으로 이를 표현할 수가 있습니다.
  • 생성자 패턴을 통해서 그 방법을 이해해 보겠습니다.

자바스크립트 객체 다시 이해하기

  • 객체리터럴(Object literal)은 비슷한 속성과 행위들을 묶어서 객체로 만드는 코딩입니다.
  • 그런데 아래 코드에서 healthObj의 형태를 가진 name이 다른 여러개의 객체가 필요하다면 어떻게 할까요?
  • healthObj2, healthObj3... 처럼 구현해도 되지만, 비슷한 객체를 계속 중복해서 만들어 두는 건 이상해보이죠.
  • 더군다나 각 객체마다 showHealth라는 메서드가 중복으로 들어가고 객체마다 같은 내용을 갖고 있기 때문에 메모리를 쓰는 차원에서도 굉장히 비효율적입니다. 게다가 코드의 중복으로 인해서 유지 보수코드를 수정할 때 굉장히 혼란스러울 것입니다.
  • 따라서 이를 좀 더 개선할 필요가 있습니다.

var healthObj = {

  name : "달리기",

  lastTime : "PM10:12",

  showHealth : function() {

    console.log(this.name + ", 오늘은 " + this.lastTime + "에 운동을 하셨네요");

  }

}

 

healthObj.showHealth();

 

객체를 동적으로 생성하는 방법

  • 먼저 객체를 동적으로 생성하는 방법을 알아보겠습니다.
    방법은 간단합니다. 아래처럼 함수를 이용하는 것입니다.

function Health(name, lastTime) {

  this.name = name;

  this.lastTime = lastTime;

  this.showHealth = function(){

    return this.name + "님 오늘은 " +this.lastTime + "에 운동을 하셨네요.";

  }

}

//var o = Health("달리기", "10:12"); //oundefined. 왜냐하면 oHealth의 반환값을 받아서 저장하는데 여기서는 반환값이 없기 때문이기에 기본값인 undefined가 반환됩니다.

const h = new Health("달리기", "10:12");

 

  • h는 객체입니다. h안을 들여다보면 어떻게 구성되어 있는지 알 수 있습니다.

h

//Health {name: "달리기", lastTime: "10:12", showHealth: ƒ}

lastTime:"10:12"

name:"달리기"

showHealth:ƒ ()

__proto__:Object

 

  • 여기서 hkey value로 구성되어 있는 객체입니다. 그리고 lastTime, name, showHealth가 바로 객체 리터럴입니다. 즉, new 키워드를 썼더니 객체가 생성이 된 겁니다.
  • 다시 Health함수를 한 번 더 불러서 h2객체를 만듭니다.

h2 = new Health("걷기", "20:11");

// Health {name: "걷기", lastTime: "20:11", showHealth: ƒ}

 

  • 계속 이런식으로 객체를 만들어낼 수 있습니다.
  • Health함수는 new키워드로 불리면서, 객체를 생성하는 함수 역할을 합니다.

function Health(name, lastTime) {

  this.name = name;

  this.lastTime = lastTime;

  this.showHealth = function(){

return this.name + "님 오늘은 " +this.lastTime + "에 운동을 하셨네요.";

}

//return this;  // 이런식으로 숨겨져 있다고 생각해도 됩니다. 그래서 new를 불렀을 때 반환이 된다고 생각하면 됩니다.

}

 

  • new 키워드가 부를 때는 return this가 내부에서 실행이 되는 것과 같이 동작한다라고 기억을 하시는 게 좋습니다그래서 Health를 생성자(constructor)라고 합니다.
  • 하지만 아직 문제가 하나 존재합니다아직 h h2를 열어보면 showHealth메서드가 여전히 중복해서 존재합니다각각의 showHealth라는 동일한 함수가 두 군데 들어있는 겁니다이처럼 동일한 메서드가 여기저기 메모리 공간을 차지하는 것은 분명한 자원 낭비입니다.
  • 이때는 생성자가 포함하고 있는 prototype 안에 두면 같은 지점에 메서드를 바라보기 때문에 h showHealth가 메모리 공간에 있는 위치랑 h2 showHealth가 메모리에 있는 위치가 같게 맞출 수가 있습니다.

prototype으로 메서드를 같이 사용해보자

  • 자바스크립트는 prototype이라는 공간을 통해서 객체간의 재사용 되는 것을 바라보게 할 수 있습니다.
    개념적으로는 이렇습니다.

  • health, 이게 인스턴스입니다. new 해가지고 불러지는 객체인데 그림에서 h, h2, h3 여러 개 선언했습니다. 그리고 해당 객체들은 prototype을 같이 공유하고 있습니다. 그러니까 prototype에다가 어떤 메서드를 넣으면 객체들은 그 공간을 같이 쉐어합니다. 예를 들어서 h.showHealth h2.showHealth는 두 개가 바라보는 위치하고 메모리 공간이 다릅니다. 그러나 showHealth를 prototype에 정의하면 h와 h2가 showHealth를 공유할 수 있으며 메모리도 효율적으로 사용이 가능합니다.
  • 얼핏보면 객체지향의 상속(inheritance)인가 싶을 겁니다실제로 비슷한 개념이라 할 수도 있습니다.
  • 다시 정리하면 위 그림에서는 각 인스턴스(h, h2, h3) prototype이라는 같은 참조객체를 바라보고 있기 때문에 prototype의 어떤 속성을 변경하면 모든 인스턴스에게 공유됩니다.

 

  • prototype에 어떠한 속성을 추가하면서 실제 코드로 확인해보겠습니다.
  • 아래 코드를 보면 Health 함수 아래 prototype 객체가 존재하고, 이것에 showHealth 메서드를 속성으로 추가했습니다이런 식으로 prototype객체 안에 여러 가지 속성을 추가할 수 있습니다.

function Health(name, lastTime) {

  this.name = name;

  this.lastTime = lastTime;

 

}

 

Health.prototype.showHealth = function() {

    console.log(this.name + "," + this.lastTime);

}

 

const h = new Health("달리기", "10:12");

console.log(h);  //크롬개발자도구를 열고 이 부분이 어떻게 출력되는지 확인해보세요

            // Health {name: "달리기", lastTime: "10:12"}

lastTime:"10:12"

name:"달리기"

__proto__:Object

h.showHealth();   // 달리기,10:12

 

  • 그럼 아래처럼 여러 인스턴스를 만들어도 prototype안의 showHealth는 같은 참조점을 바라보고 있는 것을 알 수 있습니다.

const h = new Health("달리기", "10:12");

const h2 = new Health("걷기", "14:20");

console.log(h.showHealth === h2.showHealth); //true

 

  • 이게 왜 동일하냐면 h가 가지고 있는 메서드 showHealth는 이 인스턴스의 prototype 안에 들어있는 걸 가리키고 h2Health라는 생성자 안의 prototype 안에 있는 메서드이기 때문에 같습니다.
  • 위의 메서드가 같은지 확인하기 위해선 h와 h2를 열어보면 됩니다.
  • _proto_ 안에 있다는 게 prototype 안에 있다고 생각하시면 됩니다펼쳐보면 이렇게 되어 있습니다이 관계를 이렇게 개발자 도구를 열어보면 쉽게 알 수가 있습니다.
  • h h2가 바라보는 이 showHealth prototype 안에서 찾게 돼있습니다. showHealth가 지금은 이 안에 있으니까 우리가 같은 메모리 공간을 바라보고 있기 때문에 비교를 했을 때 같은 결과가 나왔던 겁니다.
  • 굉장히 메모리 효율성이 좋은 겁니다왜냐하면 하나의 메모리 공간에 있는 걸 같이 쓰고 있기 때문입니다. 그래서 보통 메서드들은 prototype에 많이 보관을 합니다.
  • 상속을 위한 건 아니지만 사실은 prototype이라는 체인으로 자바스크립트 객체가 쭉 연결이 돼있기 때문에 상속 구조를 이걸 이용해서 만들 수도 있습니다.

 

  • prototype, new 키워드를 좀 자세히 알아보실 필요가 있습니다. 웹이나 책에서도 이 부분이 굉장히 깊고 어렵게 설명이 많이 돼있습니다. 그만큼 어려운 개념이고 약간 추상적인 느낌이라 우리가 실체를 보기는 어렵습니다.
  • 하지만 콘솔 창 같은 데서 직접 인스턴스 안의 내용을 찍어서 확인해보는 방법들이 도움이 많이 될겁니다.
  • Object.create라는 것도 있습니다. 우리가 prototype 이렇게 해가지고 만든 속성도 있지만 Object.create로도 어떤 prototype을 이용한 객체를 만들 수가 있습니다.
  • 그리고 ES6, ES2015에는 Class라는 키워드가 존재합니다. Class가 이거와 굉장히 유사하게 동작하는 코드에요. Class에는 직접 prototype에 추가하지 않아도 showHealth를 쉽게 구현할 수 있습니다. 객체지향언어에서처럼 showHealth를 메서드로 Class 안에 넣어놓으면 알아서 prototype에 들어가게 돼있어요. 그래서 prototype에 직접 정의하지 않아도 됩니다. 좀 더 편한 거죠.

생각해보기

  • prototype new 키워드를 사용해서 구현할 수 있는 객체지향 자바스크립 코드가 어떤 형태인지 찾아보면 좋겠습니다. 생각보다 다양한 패턴이 존재하며 각각의 특징을 알아가면서 prototype의 특성을 알게 될 겁니다.
  • __proto__ 라는 속성이 있습니다. 앞선 예제에서 h, h2를 이용해서 h2.__proto__ 와 같은 식으로 접근해서 결과를 확인해보세요. 직접 쓸 일은 없지만, 내부 prototype 정보를 들여다볼 수 있습니다.
  • Object.create 를 사용해서도 클래스와 같은 코드를 만들 수 있습니다.
  • ES6에서는 Class라는 키워드가 존재합니다. 어떻게 클래스를 만들고 메서드를 추가할 수 있는지 예제를 찾아서 확인해보세요. 많은 framework들이 이런 패턴으로 컴포넌트를 만들고 있습니다. 그런데 ES6 Class도 결국은 prototype을 활용해서 클래스구조를 생성한답니다. 따라서 prototype에 대한 이해는 자바스크립트를 이해하는데 꼭 알아둬야 할 개념입니다.

 

Comments