// 1. 네임스페이스와 모듈 정의
var MyApp = {} // 전역 객체
MyApp.modules = {}
// 2. 공개범위와 비공개 범위 결정
MyApp.modules.libs = (function() {
// 비공개 프로퍼티
// 비공개 메서드
return {
// 공개 멤버
}
})();
프로그램의 복잡도가 증가하면 네임스페이스도 중복될 수 있습니다. 따라서 다음과 같이 존재 여부를 확인합니다.
// 문제: 기존의 MyApp을 덮어 쓸 위험성이 있다
var MyApp = {};
// 개선: 존재 여부 확인 후 생성
if (typeof MyApp === 'undefined') {
MyApp = {};
}
// 더 잛게 작성하기
var MyApp = MyApp || {};
var module = (function () {
// 은닉할 멤버
var privateKey = 0;
function privateMethod() {
return ++privateKey; // 클로저 방식의 접근
}
// 공개할 멤버
return {
publicKey: privateKey,
publicMethod: function() {
return privateMethod();
}
}
})();
console.log(module.publicMethod()); // 1
console.log(module.publicMethod()); // 2
module에는 익명함수가 반환하는 객체가 할당되며 은닉된 멤버는 외부에서 접근할 수 없으나 publicMethod에의해 값을 변경할 수 있습니다. module.publicMethod()를 여러번 사용하는 경우 하나의 인스턴스로 privateKey를 사용하는 것과 같아 값이 매번 변경됩니다. 이것은 싱글톤의 동작과 같습니다. 단, module.publicKey와 같이 직접 찍어볼때는 문맥이 다르므로 0을 나타냅니다.
스크립트가 실행되면 '즉시실행함수' (IIFE)가 한번 실행되지만 ++privateKey처럼 사용될 때마다 매번 초기화 되는 것이 아닌 자신만의 어휘적 환경(렉시컬 환경)을 가집니다. 이것은 해당 시점의 관계되는 코드들이 유지되는것이죠.
(function($){
$.fn.myPluginName = function() {
// 추가 플러그인 구현
};
})(jQuery);
$.fn은 $.prototype입니다. 플러그인 패턴을 작성하는 또 다른 방법으로 $.extend()를 사용하는 것입니다.
(function($) {
$.extend($.fn, {
pluginType1: function() {
// pluginType1 구현
},
pluginType2: function() {
// pluginType2 구현
}
});
})(JQuery);
JQuery의 extend(첫번째_객체, {두번째_객체}, {...}...)는 두개 혹은 그 이상의 객체들을 첫번째 객체에 저장하여 병합, 확장할 수 있습니다.
// (1) 다른 스크립트나 플러그인에서 제대로 닫히지
// 않을 경우를 대비해 세미콜론(;)을 함수앞에 넣음
;(function($, window, document, undefined) {
var pluginName = 'defaultPluginName',
defaults = { // (2)
propertyName: "value"
};
// (3) 플러그인 생성자
function Plugin(element, options) {
this.element = element;
// (4) 첫번째 빈 객체 {}는 플러그인 인스턴스에 대한
// 기본 옵션을 변겅시키지 않도록 하기 위함
this.options = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
this.init();
}
// (5) 초기화
Plugin.prototype.init = function() {
// 인스턴스를 통해 DOM, options을 사용
// this.element, this.options ...
};
// (6) new Plugin() 주변에 여러개의 인스턴스 생성을
// 방지하기 위한 래퍼와 data 메소드를 이용해 cache 구성해
// 한번 생성된 인스턴스는 더 이상 같은 인스턴스를 생성하지 않는다
$.fn[pluginName] = function(options) {
return this.each(function() {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName,
new Plugin(this, options));
}
});
}
})(JQuery, window, document);
$('button.confirm').on('click', function() {
// Do it once
$('.modal').modal();
// And once more
$('.modal').addClass('active');
// And again for good measure
$('modal').css(...);
});
다중으로 제이쿼리 객체를 만들어내면 문제가 발생할 수 있습니다. 다음과 같이 체이닝을 사용하거나 캐싱을 사용합니다.
$('button.confirm').on('click', function() {
$('.modal')
.modal()
.addClass('active')
.css(...);
});
캐싱을 사용하면 다음과 같이 사용해 DOM요소에 3번을 만들어내지 않고도 단 한번 접근할 수 있습니다.
$('button.confirm').on('click', function() {
// modal 변수를 통해 캐싱
var modal = $('.modal');
modal.modal();
modal.addClass('active');
modal.css(...);
});
한번에 모든 것을 하려기 보다는 데스크를 적절히 분배하는 것이 필요합니다.
$('a.data').on('click', function() {
var anchor = $(this);
$(this).fadeOut(400, function() {
$.ajax({
// ...
success: function(data) {
anchor.fadeIn(400, function() {
// callback hell에 진입했습니다
});
}
});
});
});
각각의 역할을 분담해서 다음과 같이 개선할 수 있습니다.
var updatePage = function(el, data) {
// append fetched data to DOM
};
var fetch = function(ajaxOptions) {
ajaxOptions = ajaxOptions || {
// url: ...
// dataType: ...
success: updatePage
};
return $.ajax(ajaxOptions);
};
$('a.data').on('click', function() {
$(this).fadeOut(400, fetch);
});
"어떤 것을 완전히 알려거든 그것을 다른 이에게 가르쳐라."
- Tryon Edwards -