스태틱 멤버



// 생성자
var Gadget = (function(){

// 스태틱 변수/프로퍼티
var counter = 0,
NewGadget;
// 이 부분이 생성자를 새롭게 구현한 부분
NewGadget = function(){
console.log(counter += 1);
};

// 특권 메서드
NewGadget.prototype.getLastId = function(){
return counter;
};

// 생성자를 덮어쓴다.
return NewGadget;
}());

var iphone = new Gadget();
iphone.getLastId(); // 1
var ipod = new Gadget();
ipod.getLastId(); // 2
var ipad = new Gadget();
ipad.getLastId(); // 3


전역 생성자


네임스페이스 패턴에서는 전역 객체가 하나다. 샌드박스 패턴의 유일한 전역은 생성자다.
// 샌드박스 사용법은 다음과 같다.
new Sandbox(function (box){
// 여기에 코드가 들어감
});

다음과 같이 new를 쓰지 않고도 가상의 모듈 'ajax'와 'event'를 사용하는 객체를 만들 수 있다.
Sandbox(['ajax', 'event'], function(box){
//console.log(box);
})

// 개별적인 인자로 전달 할 수 있다
Sandbox('ajax','dom', function(box){
//console.log(box);
})

샌드박스 객체의 인스턴스를 여러 개 만드는 예제
Sandbox(['dom', 'events'], function(box) {
// box 객체는 dom, events 모듈이 결합되어 있다.

Sandbox('ajax', function(box) {
// box 객체는 ajax 모듈만 결합되어 있다.
// 이 box 객체는 바깥 쪽 box 객체와 다르다.
});
});




모듈 추가하기


실제 생성자를 구현하기 전에 모듈을 어떻게 추가할 수 있는지 확인
// Sandbox 모듈 객체
Sandbox.modules = {};

// Sandbox 모듈 dom 정의
Sandbox.modules.dom = function(box) {
box.query = function(selector, context) {};
box.queryAll = function(selector, context) {};
box.css = function(el, prop, value) {};
}

// Sandbox 모듈 events 정의
Sandbox.modules.events = function(box) {
// 필요에 따라 Sandbox 프로토타입 객체에 접근 가능
// box.constructor.prototype.prop = 'value';
box.on = function(el, type, handler, capture) {};
box.off = function(el, type, handler, capture) {};
}

// Sandbox 모듈 ajax 정의
Sandbox.modules.ajax = function(box) {
box.makeRequest = function() {};
box.getResponse = function() {};
}




생성자 구현


function Sandbox() {
// arguments를 배열로 변경한다.
var args = Array.prototype.slice.call(arguments),
// 마지막 인자는 항상 콜백 함수이다.
callback = args.pop(),
// 모듈 이름은 배열 또는 문자열로 전달될 수 있다.
modules = (args[0] && typeof args[0] === 'string') ? args : args[0],
i;

// new를 강제화하는 패턴
if ( !(this instanceof Sandbox) ) {
return new Sandbox(modules, callback);
}

// 생성된 인스턴스 객체(this)에 속성을 추가 한다.
this.prop1 = 'property 1';
this.prop2 = 'property 2';

// this 객체에 모듈을 추가한다.
// 모듈이 없거나, '*' 와일드 카드라면 모든 모듈을 사용한다.
if (!modules || modules === '*' || modules[0] === '*') {
modules = [];
for ( var module in Sandbox.modules) {
if(Sandbox.modules.hasOwnProperty(module)){
modules.push(module);
}
}
}

// 필요한 모듈을 초기화 한다.
modules.forEach(function(module, index) {
Sandbox.modules[ module ](this);
});

// 콜백 함수를 실행한다.
callback(this);
}

// Sandbox 프로토타입 객체
Sandbox.prototype = {
name: 'Application',
version: '1.0.2',
getName: function(){
return this.name;
}
// ...
};


이 구현에서 핵심적인 사항은 다음과 같다.

  • this가 Sandbox 인스턴스인지 확인 후, 생성자 함수로 호출한다. (new를 강제화하는 패턴)
  • 생성자 내부에서 this에 속성을 추가한다. 생성자의 프로토타입 객체에도 속성을 추가할 수 있다.
  • 필요한 모듈은 배열 또는 개별 문자 유형의 인자로 전달할 수 있고, * 와일드카드를 사용하거나, 쓸 수 있는 모든 모듈을 사용하겠다는 의미로 인자를 생략할 수도 있다.
  • 필요한 모듈을 모두 파악한 다음에는 각 모듈을 초기화한다. 정리하면 각 모듈을 구현한 함수를 호출해서 객체를 생성한다.
  • 생성자의 마지막 인자는 콜백 함수이다. 이 콜백 함수는 맨 마지막에 호출되며, 새로 생성된 인스턴스가 인자로 전달된다. 이 콜백 함수가 실제로 사용자의 샌드박스이며 필요한 기능을 모두 갖춘 상태에서 box 객체를 전달받게 된다.


모듈 패턴


모듈 패턴은 늘어나는 코드를 구조화하고 정리하는데 도움이 되기 때문에 널리 쓰인다.
모듈 패턴은 다음 여러 패턴 여러개를 조합한 것이다.
  • 네임 스페이스 패턴
  • 즉시 실행 함수
  • 비공개 멤버와 특권 멤버
  • 의존 관계 선언


MYAPP.namespace('MYAPP.utilties.Array');


// 모듈 정의
MYAPP.utilities.array = (function(){
// 의존관계
var uobj = MYAPP.utilities.object,
ulan = MYAPP.utilities.lang,
// 비공개 프로퍼티
ayyay_string = "[object Array]",
ops = Object.prototype.toString;

// 비공개 메서드

// var 선언 종료

// 필요시 일회성 초기화 실행

// 공개 API
return {
inArray: function(needle, haystack){
for(var i = 0, max = haystack.langth; i < max; i += 1){
if(haystackp[i] === needle){
return true;
}
}
},

isArray: function(a){
return ops.call(a) === array_string;
}
// 더 필요한 메서드 추가
}
}())



모듈 노출 패턴


모든 메서드를 비공개 상태로 유지하고 최종적으로 공개 API를 갖출 대 공개할 메서드만 골라서 노출 하는 것이다.

MYAPP.namespace('MYAPP.utilities.Array');

// 모듈 정의
MYAPP.utilities.array = (function(){
// 비공개 프로퍼티
var ayyay_string = "[object Array]",
ops = Object.prototype.toString;

// 비공개 메서드
inArray = function(needle, haystack){
for(var i = 0, max = haystack.langth; i < max; i += 1){
if(haystackp[i] === needle){
return i;
}
}
return -1;
},
isArray = function(a){
return ops.call(a) === array_string;
};
// var 선언 종료

// 공개 API
return {
inArray: inArray,
indexOd: inArray
};
}());




생성자를 생성하는 모듈


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

MYAPP.namespace('MYAPP.utilities.Array');

// 모듈 정의
MYAPP.utilities.Array = (function(){
// 의존관계
var uobj = MYAPP.utilities.object,
ulan = MYAPP.utilities.lang,

// 비공개 메서드
inArray = function(needle, haystack){
for(var i = 0, max = haystack.langth; i < max; i += 1){
if(haystackp[i] === needle){
return I;
}
}
return -1;
},
isArray = function(a){
return ops.call(a) === array_string;
},
Constr;
// var 선언 종료

// 공개 API 생성자
Constr = function(o){
this.elements = this.toArray(o);
};
// 공개 API 프로토타입
Constr.prototype = {
constructor: MYAPP.utilities.Array,
version: "2.0",
toArray: function(obj){
for(var i = 0, a = [], len = obj.length; i < len; i += 1){
ap[i] = obj[i];
}
return a;
}
}

// 생성자 함수를 반환한다
// 이 함수가 새로운 네임스페이스에 할당 될 것이다.
return Constr;
}());

// 이 생성자 함수는 다음과 같이 사용
var arr = new MYAPP.utilities.Array(obj);



모듈에 전역 변수 가져오기


변경 패턴으로는 모듈을 감싼 즉시 실행 함수에 인자를 전달하는 형태가 있다.

// 모듈 정의
MYAPP.utilities.module = (function(app, global){
// 전역 객체에 대한 참조와
// 전역 어플리케이션 네임스페이스 객채에 대한 참조가 지역 변수화 된다.
}(MYAPP, this));


비공개 프로퍼티와 메서드


자바 등 다른언어와는 달리 자바스크립트에는 private, protected, public 프로퍼티와 메서드를 나타내는 별도의 문법이 없다.

객체의 모든 멤버는 pulic, 즉 공개되어 있다.


var myobj = {
myprop: 1,
getProp: function(){
return this.myprop;
}
};
// 두개 모두 공개적으로 접근이 가능
console.log(myobj.myprop);
console.log(myobj.getProp());

// 생성자 함수를 사용해 객체를 생성할 때도 마찬가지로 모든 멤버가 공개된다.
function Gadget(){
this.name = 'iPod';
this.stretch = function(){
return 'iPod';
};
}
// 두개 모두 공개적으로 접근이 가능
var toy = new Gadget();
console.log(toy.name);
console.log(toy.stretch());



비공개 멤버


비공개 멤버에 대한 별도의 문법은 없지만 클로저를 사용해서 구현할 수 있다.
생성자 함수 안에서 클로저를 만들면 클로저 유효범위 안의 변수는 생성자 함수 외부에 노출되지 않지만 객체의 공개 메서드 안에서는 쓸 수 있다. 즉 생성자에서 객체를 반환할 때 객체의 메서드를 정의하면 이 메서드 안에서는 비공개 변수에 접근할 수 있는것이다.

function Gadget(){
// 비공개 멤버
var name = 'iPod';
// 공개된 함수
this.getName = function(){
return name;
};
}

var toy = new Gadget();

// name 은 비공개 이므로 undefied 출력
console.log(toy.name);

// 공개 메서드에서는 name에 접근할 수 있다.
console.log(toy.getName());



비공개 멤버의 허점


function Gadget(){
// 비공개 멤버
var specs = {
screen_width: 320,
screen_height: 480,
color: "white"
};

// 공개함수
this.getSpecs = function(){
return specs;
}
}

var toy = new Gadget();

// name 은 비공개 이므로 undefied 출력
console.log(toy.name);

// 공개 메서드에서는 name에 접근할 수 있다.
console.log(toy.getName());


getSpec() 메서드가 specs 객체에 대한 참조를 반환하는 문제가 있다.

var toy = new Gadget(),
specs = toy.getSpecs();
specs.color = "black";
specs.price = "free";

console.dir(toy.getSpecs());

/*
파이어폭스 로그 결과

color: "black"​
price: "free"​
screen_height: 480​
screen_width: 320​
<prototype>: Object { … }
*/



객체 리터럴과 비공개 멤버



// v1
var myobj; // 이 변수에 객체를 할당
(function(){
// 비공개 멤버
var name = "my, oh my";

// 공개될 부분을 구현
myobj = {
getName: function(){
return name;
}
}
}());

console.log(myobj.getName());

// v2
var myobj = (function(){
// 비공개 멤버
var name = "my, oh my";

// 공개될 부분을 구현
return {
getName: function(){
return name;
}
}
}());

console.log(myobj.getName());




프로토타입과 비공개 멤버


생성자를 사용하여 비공개 멤버를 만들 경우 생성자를 호출하여 새로운 객체를 만들 때마다 비공개 멤버가 매번 재생성된다는 단점이 있다.
이러한 중복을 없애고 메모리를 절약하려면 공통 프로퍼티와 메서드를 생성자의 prototype 프로퍼티에 추가해야 한다.
function Gadget(){
// 비공개 멤버
var name = 'iPod';
// 공개함수
this.getName = function(){
return name;
};
}

Gadget.prototype = (function(){
// 비공개 멤버
var browser = "Mobile Webkit";

// 공개된 프로토타입 멤버
return {
getBrowser : function(){
return browser;
}
}
}())

var toy = new Gadget();
console.log(toy.getName()); // 객체 인스턴스의 특권 메서드
console.log(toy.getBrowser()); // 프로토타입의 특권 메서드



비공개 함수를 공개 메서드로 노출 시키는 방법


노출 패턴은 비공개 메서드를 구현하면서 동시에 공개 메서드도 노출 하는것을 말한다.
var myarray;

(function(){
var astr = "[object Array]",
toString = Object.prototype.toString;

function isArray(a){
return toString.call(a) === astr;
}

function indexOf(haystack, needle){
var i = 0,
max = haystack.length;
for (; i < max; i += 1){
if(haystack[i] === needle){
return i;
}
}
return -1;
}

myarray = {
isArray: isArray,
indexOf: indexOf,
inArray: indexOf
};
}());

console.log(myarray.isArray([1,2])); // TRUE
var a = myarray.isArray({0: 1}); // FALSE
// console.log(a); // false
console.log(myarray.indexOf(["A","B","C"], "C")); // 2
console.log(myarray.inArray(["A","B","C"], "C")); // 2


네임스페이스 패턴


네임스페이스는 프로그램에서 필요로 하는 전역 변수의 개수를 줄이는 동시에 과도한 접두어를 사용하지 않고도 이름이 겹치지 않게 해준다.
어플리케이션이나 라이브러리를 위한 전역 객체를 하나 만들고 모든 기능을 이 객체에 추가하면 된다.

// 수정전 : 전역변수 5개
// 경고 : 안티패턴

// 생성자 함수 2개
function Parent(){}
function Child(){}

// 변수 1개
var some_var = 1;

// 객체 2개
var modulel = {};
modulel.data = {a:1, b:2};
var modulel2 = {};



// 수정 후 : 전역 변수 1개

// 전역 객체
var MYAPP = {};

// 생성자
MYAPP.Parent = function(){};
MYAPP.Child = function(){};

// 변수
MYAPP.some_var = 1;

// 객체 컨테이너
MYAPP.modules = {};

// 객체들을 컨테이너 안에 추가한다.
MYAPP.modules.modules1 = {};
MYAPP.modules.modules1.data = {a:1, b:2};
MYAPP.modules.modules2 = {};


이 패턴은 코드에 네임스페이스를 지정해주며, 코드 내의 이름 충돌 뿐 아니라 이 코드와 같은 페이지에 존재하는 자바스크립트 라이브러리나 위짓 등 서드 파티 코드와의 이름 충돌도 방지해준다.



범용 네임스페이스 함수


네임 스페이스를 생성하거나 프로퍼티를 추가하기 전에 먼저 이미 존재하는지 여부를 확인 하는 것이 최선이다.

// 위험
var MYAPP = {};
// 개선안
if (typeof MYAPP === "undefined"){
var MYAPP = {};
}
// 또는 더 짧게 쓸 수 있따.
var MYAPP = MYAPP || {};


이렇게 추가되는 확인 작업 때문에 상당량의 중복 코드가 생겨날 수 있다. 따라서 네임스페이스 생성의 실제 작업을 맡아 줄 재사용 가능한 함수를 만들어 두면 편하다.

var MYAPP = MYAPP || {};

MYAPP.namespace = function(ns_string){
var parts = ns_string.split('.'),
parent = MYAPP,
i;

// 처음에 중복되는 전역 객체명은 제거한다.
if(parts[0] === "MYAPP"){
parts = parts.slice(1);
}

for(i = 0; i < parts.length; i += 1){
// 프로퍼티가 존재하지 않으면 생성한다.
if(typeof parent[parts[i]] === "undefined"){
parent[parts[i]] = {};
}

parent = parent[parts[i]];
}
return parent;
};


이 코드는 다음 모든 예에서 사용할 수 있다.

// 반환 값을 지역 변수에 할당한다.
var module2 = MYAPP.namespace('MYAPP.modules.module2');
module2 === MYAPP.modules.module2; // true

// 첫부분의 'MYAPP' 을 생략하고도 쓸 수 있다.
MYAPP.namespace('modules.module51');

// 아주 긴 네임스페이스
MYAPP.namespace('once.ipon.a.time.there.was.long.nested.property');


커리(Curry)


순수한 함수형 프로그래밍 언어에서 함수는 불려지거나 호출된다고 표현하기 보다 적용된다고 표현한다.
다음은 함수 적용의 예다
** call은 인자를 하나하나씩 전달하지만 apply는 배열로 인자를 전달한다.


// 함수를 정의한다.
var sayHi = function(who){
return "hello" + (who ? ", " + who : "") + "!";
// who가 true일 경우에 "," false일 경우에 ""
};

// 함수를 호출한다.
console.log(sayHi()); // hello
console.log(sayHi('world')); // hello, world!

// 함수를 적용(apply) 한다.
console.log(sayHi.apply(null, ['hello'])); // 'hello, hello!'


예제에서 보는것처럼 함수를 적용하는 것과 호출하는 것 모두 결과는 동일하다. apply()는 두개의 매개변수를 받는다. 첫번째는 이 함수 내에 this와 바인딩할 객체이고, 두번째는 배열 또는 인자(arguments)로 함수 내부에서 배열과 비슷한 형태의 arguments 객체로 사용하게 된다. 첫번째 매개변수가 null이면 this는 전역객체를 가리킨다. 즉 함수를 특정 객체의 메서드로서가 아니라 일반적인 함수로 호출할 때와 같다.


함수가 객체의 메서드일때는 null을 전달하지 않는다


var sayHi = function(who){
return "hello" + (who ? ", " + who : "") + "!";
// who가 true일 경우에 "," false일 경우에 ""
};

var alien = {
sayHi: function(who){
return 'hello' + (who ? ', ' + who : '') + '!';
}
};

console.log(alien.sayHi('world')); // 'hello, world!'
console.log(sayHi.apply(alien, ['humans'])); // 'hello, humans!'


이 코드에서 sayHi() 내부의 this는 alien을 가리킨다. 앞선 예제에서 this는 전역 객체를 가리킨다.


apply()와 더불어 Function.prototype 객체에 call() 메서드도 있다는 것을 알아둬여한다. call() 메서드 역시 apply()와 매우 비슷하다.

함수의 매개변수가 단 하나일때는 굳이 배열을 만들지 않고 요소 하나만 지정하는 방법이 더 편하기 때문에 call()을 쓰는게 더 나을 때도 있다.


var sayHi = function(who){
return "hello" + (who ? ", " + who : "") + "!";
// who가 true일 경우에 "," false일 경우에 ""
};

var alien = {
sayHi: function(who){
return 'hello' + (who ? ', ' + who : '') + '!';
}
};

// 배열을 만들지 않는 두 번째 방법이 더 효과적
console.log(sayHi.apply(alien, ['humans'])); // 'hello, humans!'
console.log(sayHi.call(alien, 'humans')); // 'hello, humans!'



부분적인 적용


인자 전부가 아니라 일부 인자만 전달하는 방법에 대해 설명한다.
다음 코드는 가상의 partialApply() 메서드 사용법을 보여준다.

var add = function(x, y){
return x + y;
};

// 모든 인자를 적용한다.
console.log(add.apply(null, [5, 4])); // 9
// 인자를 부분적으로만 적용한다.
var newadd = add.partialApply(null, [5]);
// 새로운 함수에 인자를 적용
newadd.apply(null, [4]); // 9



커링(Curring)


커링은 함수를 변형하는 과정이다. 자바스크립트에서는 add() 함수를 수정하여 부분 적용을 처리하는 커링 함수로 만들 수 있다.

// 커링된 add()
// 부분적인 인자의 목록을 받는다.
function add(x, y){
if(typeof y === 'undefined'){ // 부분적인 적용
return function(y){
return x + y;
};
}
// 전체 인자를 적용
return x + y;
}

// 테스트
console.log(typeof add(5)); // function
console.log(add(3)(4));

// 새로운 함수를 만들어 저장
var add2000 = add(2000);
console.log(add2000(10)); // 2010


다음 예제는 이를 수행하는 범용 함수를 보여준다.

function schonfinkelize(fn){
var slice = Array.prototype.slice,
stored_args = slice.call(arguments, 1);
return function(){
var new_args = slice.call(arguments),
args = stored_args.concat(new_args);
return fn.apply(null, args);
}
}


- Array.prototype 에서 slice()메서드를 빌려와서 arguments를 배열로 바꿔 사용함
- 지역변수 slice에 slize()메서드에 대한 참조를 저장하고 stored_args에 인자를 저장함
- 이때 첫번째 인자는 커링될 함수이기 때문에 떼어냄
- 새로 반환된 함수는 클로저를 통해 비공개로 저장해둔 stored_args와 slice 참조에 접근
- 새로운 함수는 이미 일부 적용된 인자인 stored_args와 새로운 인자 new_args를 합친뒤 클로저에 저장되어 있는 fn에 적용

테스트

// 일반함수
function add(x, y){
return x + y;
}

// 함수를 커링하여 새로운 함수를 얻는다.
var newadd = schonfinkelize(add, 5);
console.log(newadd(4)); // 9

console.log(schonfinkelize(add, 6)(7)); // 13

function add2(a, b, c, d, e){
return a + b + c + d + e;
}

// 여러개의 인자를 사용할 수도 있다.
console.log(schonfinkelize(add2, 1, 2, 3)(5, 5)); // 16

// 2단계 커링
var addOne = schonfinkelize(add2, 1);
console.log(addOne(10, 10, 10, 10)); // 41
var addSix = schonfinkelize(addOne, 2, 3);
console.log(addSix(5, 5)); // 16



커링을 사용해야 할 경우


어떤 함수를 호출할 때 대부분의 매개변수가 항상 비슷하다면, 커링의 적합한 후보라고 할 수 있다.


설정 객체 패턴


설정 객체 패턴은 좀더 깨끗한 API를 제공하는 방법이다. 라이브러리나 다른 프로그램에서 사용할 코드를 만들때 특히 유용하다.
많은 수의 매개변수를 전달하기 불편할 경우 하나의 객체로 만들어 대신 전달하는 방법이 있다.

function addPerson(){ // 혹은 addPerson(obj)
// console.log(obj)
console.log(arguments);
if(arguments[0]){
console.log(arguments[0].username);
}
}

var conf = {
username : "batman",
first : "bruce",
lst : "wayne"
};

addPerson(conf);


설정 객체의 장점은 다음과 같다.

  • 매개변수와 순서를 기억할 필요가 없다.
  • 선택적인 매개변수를 안전하게 생략할 수 있다.
  • 읽기 쉽고 유지보수가 편하다.
  • 매개변수를 추가하거나 제거하기가 편하다.
설정객체의 단점은 다음과 같다.
  • 배개변수의 이름을 기억해야 한다.
  • 프로퍼티 이름은 압축되지 않는다.
이 패턴은 함수가 dom 앨리먼트를 생성할 때나 엘리먼트의 css 스타일을 지정할 때 유용하다. 엘리먼트와 스타일은 많은 수의 어트리뷰트와 프로퍼티를 가지며 대부분은 선택적은 값이기 때문이다.


함수 프로퍼티(메모이제이션)


함수는 객체이기 때문에 프로퍼티를 가질 수 있다. 함수는 처음부터 length 프로퍼티를 갖는다.

function fnc(a, b, c){}
console.log(fnc.length); // 3


다음 예제에서는 myFunc 함수에 cache 프로퍼티를 생성한다.  cache 프로퍼티는 함수로 전달된 param 매개변수를 키로 사용하고 계산의 결과를 값으로 가지는 객체다.


var myFunc = function(param){
if(!myFunc.cache[param]){
var retult = {};
// 비용이 많이 드는 수행
myFunc.cache[param] = result;
}
return myFunc.cache[param];
};

// 캐시 저장공간
myFunc.cache = {};


만약 더 많은 매개변수와 더 복잡한 타입을 갖는다면 일반적으로 직렬화하여 해결할 수 있다. 예를들어, 객체 인자를 JSON문자열로 직렬화하고 이 문자열을 cache 객체에 키로 사용할 수 있다.


var myFunc = function(){

var cachekey = JSON.stringify(
// myFync로 전달되는 argument를 받아서 array 타입으로 변환
Array.prototype.slice.call(arguments))
, retult;
if(!myFunc.cache[cachekey]){
var retult = {};
// 비용이 많이 드는 수행
myFunc.cache[cachekey] = result;
}
return myFunc.cache[cachekey];
};

// 캐시 저장공간
myFunc.cache = {};


직렬화하면 객체를 식별할 수 없게 되는것을 주의해야 한다. 만약 같은 프로퍼티를 가지는 두 개의 다른 객체를 직렬화하면 이 두객체는 같은 캐시 항목을 공유하게 될것이다.

이 함수를 작성하는 다른 방법으로 함수 이름을 하드코딩하는 대신 arguments.callee를 사용해 함수를 참조할 수 있다.

** ECMAScript 5 스트릭트 모드에서는 허용되지 않는다.


var myFunc = function(maram){

var f = arguments.callee, retult;
if(!f.cache[param]){
var retult = {};
// 비용이 많이 드는 수행
f.cache[param] = result;
}
return f.cache[param];
};

// 캐시 저장공간
myFunc.cache = {};


초기화 시점의 분기


초기화 시점의 분기는 최적화 패턴이다. 어떤 조건이 프로그램의 생명주기 동안 변경되지 않는게 확실할 경우 조건을 단 한 번만 확인하는것이 바람직하다. 브라우저 탐지가 전형적인 예다.

DOM 엘리먼트의 계산된 스타일을 확인하거나 이벤트 핸들러를 붙이는 작업도 초기화 시점 분기 패턴의 이점을 살릴 수 있는 또 다른 후보들이다.


// 인터페이스
var utils = {
addListener : null,
removeListener : null
};

// 구현
if(typeof window.addEventListener === 'function'){
utils.addListener = function(el, type, fn){
el.addEventListener(type, fn, false);
};
utils.removeListener = function(el, type, fn){
el.removeEventListener(type, fn, false);
};
} else if (typeof document.attechEvent === 'function'){ // ie
utils.addListener = function(el, type, fn){
el.attachEvent('on' + type, fn);
};
utils.removeListener = function(el, type, fn){
el.detachEvent('on' + type, fn);
};
} else { // 구형 브라우저
utile.addListener = function(el, type, fn){
el['on' + type] = fn;
};
utils.removeListener = function(el, type, fn){
eel['on' + type] = null;
};
}



즉시 객체 초기화


전역 유효범위가 난잡해지지 않도록 보호하는 또 다른 방법은 즉시 실행 함수 패턴과 비슷한 즉시 객체 초기화 패턴이다. 이 패턴은 객체가 생성된 즉시 init() 메서드를 실행해 객체를 사용한다.

({
// 여기에 설정 값(설정상수)들을 정의할 수 있다.
maxwidth: 600,
maxheight: 400,

// 유틸리티 메서드 또한 정의할 수 있다.
gimmeMax : function(){
return this.maxwidth + "x" + this.maxheight;
},

// 초기화
fnInit: function(){
console.log(this.gimmeMax());
// 더 많은 초기화 작업
}

}).fnInit(); // 초기화 함수 실행


문법적인 면에서 이 패턴은 객체 리터럴을 사용한 일반적인 객체 생성과 똑같이 생각하면 된다. 객체 리터럴도 괄호로 감싸는데 이는 자바스크립트 엔진이 중괄호를 코드블록이 아니라 객체 리터럴로 인식하도록 지시하는 역할을 한다.

객체만 괄고로 감싸는게 아니라 객체와 init() 호출 전체를 괄호 안에 넣을 수도 있다.


({
//...
}).init();

({
//...
}.init());


이 패턴의 장점은 즉시 실행 함수 패턴의 장점과 동일하다. 단 한 번의 초기화 작업을 실행하는 동안 전역 네임스페이스를 보호할 수 있다.

+ Recent posts