본문 바로가기

JavaScript

자바스크립트 코딩기법과 핵심패턴 제 5장 객체 생성 패턴

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


 

요즘 개인적으로 자바스크립트를 공부하고 있다. 하지만 내 경우 어정쩡하게 알고 있는 자바스크립트라 기초책은 보나마나인데, 이 책은 정말 실무에서도 바로 쓸 수 있는 패턴을 뽑아서 먹여주는 책 같다. 이 책과 더불어 자바스크립트 성능 최적화도 보면 정말 좋겠다. 아무튼 이 책을 요약하면서 정리하고자 한다. 이 책은 정말 강력 추천하며 자바스크립트를 제대로 학습하기 위한 필수 소장서이다.

책구입 : http://tinyurl.com/7ejd4rs
출판사 책소개 : http://blog.insightbook.co.kr/245


자바스크립트 코딩기법과 핵심패턴 제 5장 객체 생성 패턴

자바스크립트에서는 객체 리터럴이나 생성자 함수를 사용하여 쉽게 객체를 만들 수 있다. 자바스크립트는 네임스페이나, 모듈패키지, 비공개 프로퍼티, 스태틱 맴버 등에 대한 문법이 없기 때문에 이러한 기능을 구현하거나 대체하는 범용적인 패턴이 존재한다.

네임스페이스 패턴


장점

  • 전역 공간을 깨끗하게 유지하고 코드를 구조화하여 정리를 도와주는 패턴.


사용법

  • 전역 네임스페이스의 객체의 이름은 애플리케이션 이름이나 라이브러리의 이름, 도메인명, 회사 이름중 선택해서 사용한다.
  • 눈에 띄기 쉽도록 전역 객체 이름은 모두 대문자로도 사용한다.


단점

  • 모든 변수와 함수에 접두어를 붙여야 하므로 전체적으로 코드량이 늘어남
  • 전역 인스턴스가 하나뿐이므로 코드의 어느 한부분이 수정되면 전역 인스턴스를 수정하게 된다.
  • 이름이 중첩되고 길므로 프로퍼티 판별하기 위한 검색 작업이 느리다. (샌드박스 패턴으로 해결)


범용 네임스페이스 함수

 
01.var MYAPP = MYAPP || {};
02. 
03.MYAPP.ns = function (ns_string) {
04.    var parts = ns_string.split('.');
05.        parent = MYAPP,
06.        i;
07.    // 처음에 중복되는 적역 객체명은 제거
08.    if( parts[0] === 'MYAPP' ){
09.        parts = parts.slice(1);
10.    }
11.    for (i = 0; i < parts.length; i += 1) {
12.        if ( typeof parent[parts[parts[i]] === "undefined") {
13.            parent[parts[i]] = {};
14.        }
15.        parent = parent[parts[i]];
16.    }
17.    return parent;
18.};
19. 
20.//반환 값을 지역변수에 할당
21.var module2 = MYAPP.ns('MYAPP.modules.modules2');
22.modules2 === MYAPP.modules.modules2; //true
23. 
24.//첫 부분의 'MYAPP' 생략가능
25.MYAPP.ns('modules.modules51');



 

의존 관계 선언


네임스페이스를 지정하여 모듈화함으로써 필요한 모듈만 골라서 쓸 수 있게 되었다. 모듈 사용시 함수나 모듈 내 최상단에, 의존 관계에 있는 모듈을 지역변수로 그 모듈을 가리키도록 선언하는 것이 좋다.
사용법

 
1.var myFunction = function () {
2.    //의존 관계가 있는 모듈들
3.    var event = YAHOO.util.Event,
4.        dom = YAHOO.util.Dom;
5.    //이제 event, dom을 사용
6.};



장점

  • 의존 관계가 명시적이다.
  • 지역변수로 했기 때문에 전역변수와 중첩 프로퍼티 탐색에서 속도적 이익이 있다.
  • 지역변수명은 압축도구(YUICompressor, Google Closure)등으로 축약된다.



 

비공개 프로퍼티와 메서드


자바스크립트는 프로퍼티를 공개권한을 설정할 수 있는 명령이 존재하지 않고 기본적으로 모두 public이 된다.

비공개(private) 맴버
클로저를 사용하여 비공개 맴버를 구현할 수 있다.

 
01.function Gadget() {
02.    //비공개 맴버(외부에서 지역변수를 접근할 수 없으므로)
03.    var name = 'iPad';
04.    //공개된 함수
05.    this.getName = function () {
06.       return name;
07.    }
08.}
09.var toy = new Gadget();
10.console.log(toy.name); //undefined
11.console.log(toy.getName()); //iPad



특권(privileged) 메서드
비공개 멤버에 접근권한을 가진 공개 메서드를 가리키는 이름이다. 위 예제에서는 getName() 메서드가 그렇다.

비공개 멤버의 허점
특권 메서드에서 비공개 변수의 값을 반환할 경우 이 변수가 객체나 배열이라면 값이 아닌 참조가 반환되므로 외부 코드에서 비공개 변수값을 수정할 수 있다는 허점이 존재한다. 허점을 피하기 위해 최소 권한의 원칙(principle of Least Authority, POLA)를 적용하여 필요 이상의 권한을 주지않도록 한다. 가령 일부 데이터만 반환하던가, 객체를 복사해서 반환하던가 하는 방식을 취한다.

객체 리터럴과 비공개 맴버
비공개 맴버를 만드는데 생성자 방법 뿐 아니라 리터럴로도 만들 수 있다. 즉, 익명 즉시 실행 함수를 추가하여 클로저를 만들면 되겠다. 이것은 모듈 패턴의 기초가 된다.

 
01.var myobj = (function () {
02.    //비공개 멤버
03.    var name = "my, oh my";
04. 
05.    //공개될 부분을 구현
06.    return {
07.       getName : function () {
08.           return name;
09.       }
10.    };
11.}());
12.myobj.getName(); //my, oh my


 

프로토타입과 비공개 맴버


생성자를 사용해 비공개 맴버를 만들 경우, 생성자를 호출하여 새로운 객체를 만들 때마다 비공개 멤버가 매번 재생성되어 메모리 낭비가 생긴다. 그러므로 메모리를 절약하기 위해 공통 프로퍼티와 메서드를 생성자의 프로토타입에 추가해야 한다.

 
01.function Gadget() {
02.    // 비공개 맴버
03.    var name = 'iPod';
04.    // 공개 함수
05.    this.getName = function () {
06.       return name;
07.    };
08.}
09.Gadget.prototype = (function () {
10.    // 비공개 멤버
11.    var browser = "Mobile Webkit";
12.    // 공개된 프로토타입 맴버
13.    return {
14.        getBrowser: function () {
15.            return browser;
16.        }
17.    };
18.}());
19. 
20.var toy = new Gadgets();
21.console.log(toy.getName());  // 객체 인스턴스의 특권 메서드
22.console.log(toy.getBrowser());  ///프로토타입의 특권 메서드



비공개 함수를 공개 메서드로 노출하는 방법
노출 패턴(revelation pattern)은 비공개 메서드를 구현하면서 동시에 공개 메서드로도 노출하는 것을 의미. 비공개 함수를 안전하게 보호해줌

 
01.var myarray;
02.(function () {
03.    //비공개 맴버
04.    var astr = "[object Object]",
05.        toString = Object.prototype.toString;
06. 
07.    //비공개 함수
08.    function isArray() {
09.        return toString.call(a) === astr;
10.    }
11. 
12.    //비공개 함수
13.    function indexof(haystack, needle) {
14.       ....
15.    }
16. 
17.    myarray = {
18.        isArray : isArray,
19.        indexOf: indexOf,
20.        inArray: indexOf
21.    };
22.}());
23. 
24.myarray.indexOf = null;
25.myarray.inArray(["a", "b", "z"], "z"); //2


이처럼 결과가 나오는 것은 비공개 함수는 보호되기 때문이다.

모듈패턴


 

  • 모듈 패턴은 늘어나는 코드를 구조화 하고 정리하는데 도움이 되고 널리 쓰인다.
  • 이 패턴을 사용하면 개별적인 코드를 느슨하게 결합시킬 수 있다.
  • 모듈 패턴은 특히 점점 늘어만 가는 코드를 정리할 때 널리 사용되며 매우 추천하는 방법이다.
  • 모듈 패턴은 네임스페이스 패턴, 즉시 실행 함수, 비공개 맴버와 특권 맴버, 의존 관계 선언, 노출 패턴등이 섞인 패턴이다.
 
01.//네임스페이스 패턴 적용
02.MYAPP.ns('utilities.array');
03. 
04.//즉시실행함수
05.MYAPP.utilities.array = (function () {
06.    //의존 관계 선언
07.    var uobj = MYAPP.utilities.object,
08.        ulang = MYAPP.utilities.lang;
09.    //비공개 프로퍼티
10.    var array_string = "[object Array]",
11.        ops = Object.prototype.toString,
12. 
13.    ///비공개 매서드(특권 메서드)
14.        inArray = function (haystack, needle) {
15.        },
16.        isArray = function (a) {
17.        };
18. 
19.    //공개 API 노출(노출패턴 적용)
20.    return {
21.        isArray: isArray,
22.        indexof: inArray
23.    };
24.}());


생성자를 생성하는 모듈
앞선 예제는 MYAPP.utilities.array 라는 객체를 만들었지만 생성자 함수를 사용해 객체를 만드는게 더 편할 수 있다.

 
01.MYAPP.ns('MYAPP.utilities.Array');
02.MYAPP.utilities.Array = (function () {
03.    //의존 관계 선언
04.    var uobj = MYAPP.utilities.object,
05.        ulang = MYAPP.utilities.lang;
06.    // 비공개 프로퍼티와 메서드 선언 후...
07.        Constr;
08.    //var 선언 마침
09. 
10.    //공개 API - 생성자 함수
11.    Constr = function (o) {
12.        this.elements = this.toArray(o);
13.    };
14.    //공개 API - 프로토타입
15.    Constr.prototype = {
16.        constructor: MYAPP.utilities.Array,
17.        version: "2.0",
18.        toArray : function(obj) {
19.            ...
20.        };
21.    };
22. 
23.    //생성자 함수를 반환
24.    //이 함수가 새로운 네임스페이스에 할당될 것이다.
25.    return Constr;
26.}());
27.var arr = new MYAPP.utilities.Array(obj);


모듈에 전역 변수 가져오기
보통 전역변수에 대한 참조 또는 전역 객체 자체를 전달한다. 이렇게 하면 좀더 탐색이 빨라짐

 
1.MYAPP.utilities.module = (function (app, global) {
2.}(MYAPP, this));


 

샌드박스 패턴


다음 네임스페이스의 단점을 해결한다.

  • 애플리케이션 전역 객체가 단 하나의 전역변수에 의존
  • 항상 긴 이름을 써야해서 런타임시 탐색 과정을 거쳐야 함.
  • 다음과 같은 인터페이스에 대응한다.
 
01.Sandbox(['ajax', 'event'], function (box) {
02.    // console.log(box);
03.});
04. 
05.Sandbox('ajax', 'event', function (box) {
06.    // console.log(box);
07.});
08. 
09.Sandbox('*', function (box) {
10.    // console.log(box);
11.});
12. 
13.Sandbox(function (box) {
14.    // console.log(box);
15.});
16. 
17.Sandbox('dom', 'event', function (box) {
18.    Sandbox('ajax', function (box) {
19.    });
20.});

샌드박스 패턴을 사용하면 콜백 함수로 코드를 감싸기 때문에 전역 네임스페이스를 보호할 수 있다.

스태틱 맴버


자바스크립트는 스태틱 맴버를 만들기 위해 별도의 패턴이 필요하다.

  • 스태틱 맴버는 공개와 비공개 맴버가 있다.
  • 특정 인스턴스에 한정되지 않는 메서드와 데이터를 담을 수 있고 인스턴스별로 매번 생성하지 않는다.
  • 비공개 스태틱 맴버는 싱글턴 패턴을 다룰 때 사용된다.


 

객체 상수


자바스크립트는 상수는 없지만 값을 한번 설정하면 변경되지 않도록 만들 수 있다.

체이닝 패턴


채이닝 패턴이란 객체에 연쇄적으로 메서드를 호출할 수 있도록 하는 패턴이다. 아이디어는 this를 반환한다.

 
1.myobj.method1("hello").method2().method3("world").method4();



체이닝 패턴의 장단점

  • 코드량이 줄고 코드가 간결해진다.
  • 작고 특화된 함수를 만들어 유지보수에 도움이 된다.
  • 하지만 디버깅이 어렵다.

method() 메서드


아래처럼 체이닝 패턴을 이용해 method를 등록할 수 있게 한다. 이때 인스턴스 맴버가 아닌 프로토타입에 등록시키기 위해 method는 그 아래 코드처럼 구현한다.

 
1.var Person = function (name) {
2.    this.name = name;
3.}.method('getName', function() {
4.    return this.name;
5.}).method('setName', function(name) {
6.    this.name = name;
7.    return this;
8.});

method는 아래처럼 구현한다. 결국 Function의 프로토타입에 method가 만들어져 확장된다.

 
1.if( typeof Function.prototye.method !== "function") {
2.   Function.prototype.method = function (name, implementattion) {
3.       this.prototype[name] = implementation;
4.       return this;
5.   };
6.}