앞에 쓰는 말
실제로 여기의 모듈 패턴은 더글러스가 제안한 Module 패턴으로, 완전히 같지만, 저자는 주류 JS 라이브러리 (프레임워크) 의 모듈 관리를 분석했으며, 이것이亮点라고 할 수 있습니다
一.모듈 패턴
JS 에는 class 의 개념이 없으며, 당연히基于类의 캡슐화라는 말도 없습니다. class 의 최대 장점은 아마도모듈 (네임스페이스) 관리와 액세스 제어일 것이지만, 이것들은 JS 에는 없습니다. 그러나 JS 함수 스코프의 특징을 이용하여 액세스 제어 (캡슐화) 를 실현할 수 있습니다. 말하는 코드는 다음과 같습니다:
var module = (function() {
// 私有属性
var attr1 = 1;
function fun1() {
// ...
}
// 返回匿名对象,当然,也可以返回不匿名的对象
return {
// 公有属性
attr2: attr1,
fun2: fun1
}
})(); // IIFE, 匿名函数立即执行
외부에서는公有属性 attr2 와 fun2 에만 액세스할 수 있으며, 私有属性 attr1 와 fun1 은 액세스가 제한됩니다 (undefined). 이렇게 보면 캡슐화는 양호하다고 할 수 있습니다. 그러나 문제는 module 이 싱글톤이며, 재사용할 수 없다는 것입니다. 재사용하고 싶다면, 조금 변경해야 합니다:
function Module() {
// 私有属性
var attr1 = 1;
function fun1() {
// ...
}
// 公有属性
this.attr2 = attr1;
this.fun2 = fun1;
}
var module1 = new Module();
var module2 = new Module();
액세스 제어 (캡슐화) 의 관점에서 보면, 두 방식 모두 가능합니다.第一种 방식의 단점은 싱글톤의 제한이며, 초기화 시 파라미터를 전달할 수 없는 등의 문제도 존재합니다.第二种 방식은 재사용을 지원하지만, 여러 인스턴스의 오버헤드도 존재합니다. 구체적인 사용은 장면에 따라 결정해야 합니다
P.S.더글러스의 Module 패턴에 대한 상세 정보는, 黯羽轻扬:JS 학습 노트 3_ 함수식 참조
二.Module 패턴의 변화
###1.믹스인 도입
(function(m, arg){
// ...
})(Module, arg);
이는 실제로는 모듈 패턴과는あまり 관계가 없으며, 중점도 저자가 강조하는「별명」이 아니라, 「모듈 파일화」中的一种 중요한 방법입니다. AMD, CMD 에서는 모두 단일 모듈이 단일 파일에 대응하며, 이 방식은 다른 파일에 있는 모듈을 확장하는 방법을 제공합니다. (「믹스인 도입」? 보지 않은 것으로 치죠。。)
위의 방식에는 한 점 문제가 있어, 모듈을 엄격하게 순서대로 로드해야 한다는 것입니다 (먼저 Module 오브젝트 정의를 로드해야 함). 아래의 방식으로 제한을 해제할 수 있습니다:
(function(m, arg){
// ...
})(Module || {}, arg);
이 방식을 사용하면 모듈의 비동기 로드를 실현할 수 있지만, 다른 모듈에 의존하는 모듈에는 적용할 수 없습니다
###2.도출
(function() {
// 创建
var obj = {};
// 增强
obj.attr = 1;
// 返回
return obj;
})();
특별한 곳은 없습니다.值得一提的是더글러스의 모듈 패턴 사상:작성 -> 강화 -> 반환. 「도출」에 대해서는, 나도 저자가 무엇을 말하고 싶은지 모르겠습니다。。
三.JS 라이브러리 (프레임워크) 의 모듈 관리
###1.Dojo 모듈
obj.setObject('module.submod.submod', (function() {
// 私有属性
// var
// function
return {
// 公有属性
// attr: val
};
})();
매우 특별한 모듈 정의 방식으로, setObject(str, obj) 입니다.実は 더욱 편리한 방식도 있습니다:
String.prototype.ns = function (obj) {
return setObject(this.toString(), obj);
};
때로는 JS 가 제공하는 유연성을 충분히 활용하는 것은 매우 편리합니다 (프로토타입을 확장하는 것으로 인한 문제를 과도하게 걱정할 필요는 없습니다. ns 가 충돌할 가능성이 있다면, regns, regNS 로 바꾸면。。)
###2.ExtJS 모듈
Ext.namespace('module');
module.submod = (function() {
// 私有属性
// var
// function
return {
// 公有属性
// attr: val
};
})();
기본적인 모듈 패턴과 매우 비슷하며, 쉽게 실현할 수 있습니다
###3.YUI 모듈
Y.namespace('module.submod') = (function() {
// 私有属性
// var
// function
return {
// 公有属性
// attr: val
};
})();
실현 방식은 모두 비슷하며, 이렇게 실현하면 체인 호출을 지원합니다: Y.namespace('module.submod').attr = val;
###4.JQuery 모듈
function regLib(mod) {
$(function() {
if (mod.init) {
mod.init();
}
});
return mod;
}
var module = regLib((function() {
// 私有属性
// var
// function
return {
// 公有属性
// attr: val
init: function() {
// ...
}
};
})());
regLib 함수를 이용하여, 새로운 모듈을 로드할 때 자동으로 mod.init() 를 DOMready 이벤트 핸들러에 배치합니다. 이것도 특징이라고 할 수 없습니다.実は JQuery 가 제공하는 플러그인 메커니즘이 모듈 관리입니다. 여기서는 플러그인 메커니즘을 사용하지 않았지만, JQuery 중에서 비플러그인 방식으로 커스텀 모듈을 관리하는 방법을 제공하고 있습니다. JQuery 플러그인 메커니즘은 다음과 같습니다:
;(function ($) {
// 私有属性
// var
// function
$.fn.myPlugin = {
// 公有属性
// attr: val
version: 1.0
};
})(jQuery);
본질적인 차이는 없으며, 모두套路입니다
四.Revealing Module(揭示 모듈) 패턴
앞에서 언급한 모듈 패턴과의 유일한 차이는, 公有属性이 私有属性의 참조であることを 요구하는 것입니다.実は第一例의 코드는 이미 이렇게 하고 있습니다. 이 한 점만으로 그것을 새로운 패턴으로 하는 것은 가치가 없을지도 모르므로, 이 부분 내용은 여기에 쑤셔 넣었습니다.
모듈 내부는 매우 유연하게 처리할 수 있으며, 익명 함수의 참조, 私有属性의 참조, 또는 기본 값을 직접 공개하는 것을 선택할 수 있습니다. 무엇인가의「揭示 모듈 패턴」을 만족시키기 위해, 이 유연성을 포기할 필요는 없습니다.
五.고급 모듈 패턴
P.S.이 부분 내용은 필자가 보충한 것으로, 다행히 책의 내용이 많지 않아, 필자가 이 기사를 찾을 기회가 있었습니다: 깊이 이해하는 JavaScript 모듈 패턴
###1.얕은 복사
위에서 언급한 모든 실현 방식은 직접 모듈 오브젝트를 확장하는 것입니다. 원래 모듈 오브젝트의的基础上에서 독립적인 모듈을 만들고 싶다면, 얕은/깊은 복사가 필요합니다. 예를 들어:
var MODULE_TWO = (function (old) {
var my = {},
key;
for (key in old) {
if (old.hasOwnProperty(key)) {
my[key] = old[key];
}
}
var super_moduleMethod = old.moduleMethod;
my.moduleMethod = function () {
// override method on the clone, access to super through super_moduleMethod
};
return my;
}(MODULE));
위의 예는 얕은 복사로, 참조 타입의 속성은 여전히 공유됩니다. 완전히 독립하고 싶다면, 깊은 복사必须进行
###2.크로스 파일 私有 상태
이는 매우 특별한 방법으로, [黯羽轻扬:《JavaScript 语言精粹》之函数化](/articles/《javascript 语言精粹》之函数化/) 에서 언급한「函数化的 사상」과 유사하며, 이基础上에 액세스 제어를 추가했습니다. 예제 코드는 다음과 같습니다:
var MODULE = (function (my) {
var _private = my._private = my._private || {},
// 模块加载前,开启对_private 的访问,以实现扩充部分对私有内容的操作
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
},
// 模块加载后,调用以移除对_private 的访问权限
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
};
// permanent access to _private, _seal, and _unseal
return my;
}(MODULE || {}));
하나의 모듈이 몇 개의 파일로 나뉠 때, 모듈 로드 전에 _unseal 을 호출하여 私有属性에 대한 액세스 권한을 취득하고, 모듈 로드 완료 후에 _seal 을 호출하여 액세스 권한을 삭제합니다. _private 은《JavaScript 语言精粹》函数化 부분 중의 my 컨테이너입니다
참고 자료
-
『JavaScript 디자인 패턴』
-
깊이 이해하는 JavaScript 모듈 패턴: 몇 가지 고급 모듈 패턴에 언급하고 있으며, 매우 좋음
-
[黯羽轻扬:《JavaScript 语言精粹》之函数化](/articles/《javascript 语言精粹》之函数化/)
아직 댓글이 없습니다