티스토리 뷰
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"); //o는 undefined. 왜냐하면 o는 Health의 반환값을 받아서 저장하는데 여기서는 반환값이 없기 때문이기에 기본값인 undefined가 반환됩니다. const h = new Health("달리기", "10:12"); |
- h는 객체입니다. h안을 들여다보면 어떻게 구성되어 있는지 알 수 있습니다.
h //Health {name: "달리기", lastTime: "10:12", showHealth: ƒ} lastTime:"10:12" name:"달리기" showHealth:ƒ () __proto__:Object |
- 여기서 h는 key와 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 안에 들어있는 걸 가리키고 h2도 Health라는 생성자 안의 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에 대한 이해는 자바스크립트를 이해하는데 꼭 알아둬야 할 개념입니다.
'부스트코스 웹 프로그래밍 > 5. 웹 앱 개발: 예약서비스 3' 카테고리의 다른 글
4. 상태유지기술(Cookie & Session) - BE (1) (0) | 2019.08.09 |
---|---|
3. form 데이터 보내기 - FE (2) (0) | 2019.08.09 |
3. form 데이터 보내기 - FE (1) (0) | 2019.08.09 |
2. JavaScript Regular expression - FE (0) | 2019.08.08 |
1. UI Component module - FE (2) (0) | 2019.08.08 |