// 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);
});
"If you would thoroughly know anything, teach it to other."
- Tryon Edwards -