자바스크립트 싱글톤
작업하다가 싱글톤으로 짜여진 소스를 볼 수 있었다.
유명하긴 하지만 처음 본 디자인패턴이라 본김에 정리하고 익숙해 져야겠다.
싱글톤패턴은 클래스(생성자함수)의 인스턴스를 오직 하나만 유지한다.
동일 클래스로 객체를 여러개 생성해도 최초 생성된 객체하나만을 얻게된다.
사실 자바스크립트는 클래스가 없기때문에 객체는 다른 객체와 같지않기 때문에 이미 그 자체로 싱글톤이다.
싱글톤의 특징은
- 객체 자체에는 접근이 불가능하다.
- 객체에 대한 접근자를 사용해서 실제 객체를 제어할 수 있다.
- 객체는 단 하나만 만들어지며, 해당 객체를 공유한다.
1 2 3 |
var uni = new Universe(); var uni2 = new Universe(); uni === nui2 // true |
이런식이다.
최초 인스턴스의 참조 this를 생성자가 캐시한 후 다음번 생성자 호출때 캐시된 인스턴스를 반환하게 한다.
최초 인스턴스를 저장하기위해 전역변수를 쓰거나 생성자의 static 프로퍼티에 인스턴스를 저장할 수도 있다. 그리고 인스턴스를 클로저로 감쌀 수 도 있다. 마지막 방법은 클로저를 사용하는 방법으로 인스턴스를 비공개로 저장해 외부에서 수정할 수 없게한다.
생성자의 static 프로퍼티에 인스턴스 저장하기
1 2 3 4 5 6 7 8 9 10 11 12 |
function Universe(){ if( typeof Universe.instance === "object" ){ return Universe.instance; } this.property1 = "p1"; this.property2 = "p2"; Universe.instance = this; } |
클로저에 인스턴스 저장하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function Universe(){ var instance = this; this.property1 = "p1"; this.property2 = "p2"; // 최초 호출이후 생성자를 다시 초기화한다. // 자기자신을 재정의하는 함수패턴 // 단 재정의 시점에서 이전에 원본 생성자의 prototype 객체를 참조하는__proto__ 링크를 잃어버린다. Universe = function(){ return instance; }; } |
위 방법은 생성자 함수 재정의 시점에서 이전에 원본 생성자의 prototype 객체를 참조하는 __proto__ 프로퍼티를 잃어버린다. 따라서 생성자의 프로토타입에 무언가를 추가해도 더 이상 원본생성자로 생성된 인스턴스와 연결되지 않는다.
1 2 3 4 5 6 7 8 9 10 |
Universe.prototype.nothing = true; var uni = new Universe(); Universe.prototype.something = false; var uni2 = new Universe(); uni.nothing // true uni2.something // undefined or uncaught type error |
그리고 constructor를 사용하여 생성자를 확인할 경우 uni.constructor는 재정의된 생성자가 아닌 Universe() 원본 생성자를 가르키고 있기 때문에 아래와 같은 결과를 확인할 수 있다.
1 2 |
uni.constructor.name; // Universe uni.constructor === Universe; // false |
잃어버린 프로토타입과 생성자 포인터가제대로 동작하려면 아래와 같이 작성하면된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
function Universe(){ // 인덱스 캐시 var instance; // 생성자 재작성 Universe = function Universe(){ return instance; }; // prototype 참조변경 Universe.prototype = this; // 인스턴스 instance = new Universe(); // 생성자 포인터 재설정 instance.constructor = Universe(); // instance.prop1 = "p1"; instance.prop2 = "p2"; return instance; } /*===================================================================*/ Universe.prototype.nothing = true; var uni = new Universe(); Universe.prototype.something = false; var uni2 = new Universe(); uni.nothing // true uni2.something // false uni.constructor.name; // Universe uni.constructor === Universe; // true |
아래는 싱글톤 개인적으로 연습해본 소스다. 다른분이 툴팁을 예로 말씀해주셔서 짜보긴 했는데 사실 굳이 싱글톤 패턴이 필요치 않을 것 같긴하다. 싱글톤패턴이 UI개발 어디에 활용되면 좋을지 아직은 감이 안온다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
var Singleton = function(obj){ // set instance, cache this var _instance = null, _that = this; // 싱글톤 생성자 재선언 Singleton = function(){ return _instance; }; // 싱글톤 생성자 셋팅 Singleton.prototype = this; _instance = new Singleton(); _instance.constructor = Singleton(); // 싱글톤의 멤버는 this, prototype 둘중 아무거나 참조해도 상관없다. // 어차피 단일 인스턴스 생성이기 때문에 멤버를 공유하기위한 prototype 객체가 의미가 없다. // 주석및 의미있는 객체단위로 묶어서 작성하면 디버깅할때 편할 듯 _that.fn = { drawToolTip : function(){ $(this).append(_that.dom.fragment.tooltip) } }; _that.dom = { cached : { target : $(obj) }, fragment : { tooltip : "<div class='tipLayer'>{{sToolTip}}</div>" } }; // 이벤트 바인딩 _that.dom.cached.target.on("mouseover", _that.fn.drawToolTip); // 싱글톤 반환, 사실 위에서 리턴문이 있지만 이부분이 없으면 최초 싱글톤 호출시 인스턴스가 반환되지 않는다. // 두번재 부터는 어차피 싱글톤 생성자가 재정의되어서 생성된 인스턴스를 반환하는 역할만함 return _instance; }; $(function(){ var tt = new Singleton(".btn1"); }) |