본문으로 건너뛰기

모듈 패턴_JavaScript 디자인 패턴2

무료2015-07-12#JS#Design_Pattern#模块模式#Module_Pattern#揭示模块模式#Revealing_Pattern.高级模块模式

소위 Module(모듈) 패턴이란, 실제로는 JS 를 위한 일종의 캡슐화 사상이다. 많은 언어는 액세스 제어 메커니즘 (public, private 등) 을 제공하지만, JS 에는 없어서 모듈 패턴이 생겼다

앞에 쓰는 말

실제로 여기의 모듈 패턴은 더글러스가 제안한 Module 패턴으로, 완전히 같지만, 저자는 주류 JS 라이브러리 (프레임워크) 의 모듈 관리를 분석했으며, 이것이亮点라고 할 수 있습니다

一.모듈 패턴

JS 에는 class 의 개념이 없으며, 당연히基于类의 캡슐화라는 말도 없습니다. class 의 최대 장점은 아마도모듈 (네임스페이스) 관리와 액세스 제어일 것이지만, 이것들은 JS 에는 없습니다. 그러나 JS 함수 스코프의 특징을 이용하여 액세스 제어 (캡슐화) 를 실현할 수 있습니다. 말하는 코드는 다음과 같습니다:

var module = (function() {
    // 私有属性
    var attr1 = 1;
    function fun1() {
        // ...
    }

    // 返回匿名对象,当然,也可以返回不匿名的对象
    return {
        // 公有属性
        attr2: attr1,
        fun2: fun1
    }
})();   // IIFE, 匿名函数立即执行

외부에서는公有属性 attr2fun2 에만 액세스할 수 있으며, 私有属性 attr1fun1 은 액세스가 제한됩니다 (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 컨테이너입니다

참고 자료

댓글

아직 댓글이 없습니다

댓글 작성