Шаблон для шаблона модуля Javascript и инициализации подмодуля

Я запускаю новый проект, и я пересматриваю свои лучшие практики, чтобы попытаться предотвратить любые проблемы, а также посмотреть, какие вредные привычки я получил.

Я не очень доволен тем, как я обрабатываю последовательности инициализации в Javascript, используя шаблон модуля/подмодуля.

Скажем, мой код заканчивается чем-то вроде

FOO.init()
FOO.module1.init()
FOO.module2.init()
FOO.module3.init()
FOO.module4.init()

в глобальной области.

Я по существу делаю (проверка ошибок и подробности опущены для краткости):

var FOO = (function (me) {
    me.init = function () {
        for (var i in me.inits) {
            me.inits[i]();
        }
    }

    return me; 
}(FOO || {}));

var FOO = (function (parent) {
    var me = parent.module1 = parent.module1 || {};

    me.init = function () {
    }

    parent.inits.push(me.init);

    return parent;
}(FOO || {}));

$(document).ready(FOO.init);

для инициализации.

Я знаю, что я читал об этом раньше, но я не могу найти правильные условия поиска, чтобы найти статьи сейчас. Есть ли хорошо продуманный и проверенный шаблон, который обрабатывает инициализацию в синтаксисе, подобном этому?

Спасибо.

EDIT: перечитав это, я думаю, что небольшой контекст будет отвечать на вопросы.

В моем случае каждый модуль/подмодуль находится в собственном файле. Базовый модуль определяет основные функции сайта, а подмодули допускают различные функции. Например, подмодуль может подключить автозаполнение в окне поиска, а другой может превратить статическое изображение заголовка во вращающийся баннер. Подмодули включены/отключены CMS, поэтому я действительно хочу развести явные вызовы внутри базового модуля, чтобы все могло управляться CMS. Я также считаю, что существуют специальные способы CMS для этого, но я ищу общий шаблон Javascript для этого, чтобы обеспечить согласованность и устойчивость проектов, которые могут использовать другую CMS.

Ответы

Ответ 1

У меня лично есть другой стиль кодирования. Это одна из них. Другой - это в основном имитация стиля, используемого в backbone.js

var myProgram = (function() {
   var someGlobal, someGlobal2;   

   var subModule1 = (function() {
       ...       

       var init = function() {

       };

       ...

       init();

       return { 
           "someMethod": someMethod,
           ...
       };
   }());

   var OtherSubModule = (function() {
       ...
       var init = function(param) { ... };
       ...
       return { 
           "init": init,
           ...
       };
   }());

   var init = function(param) {
       ...

       OtherSubModule.init({
           "foo": param.foo,
           "bar": param.bar,
           ...
       });
   };


   return { 
       "init": init,
       "somePublic": OtherSubModule.foobar, 
       ...
   }
}());

Зависит от того, нужно ли мне предоставлять публичный API другим пользователям, что делает его намного лучше. Я предпочитаю создавать модули, управляемые функцией init для начальной конфигурации, а остальное полностью управляемое событием.

[изменить]

Учитывая отредактированный вопрос, у меня есть другой шаблон для этого. Каждый файл определяет функцию на каком-то объекте, в моем случае это было $.FooBar.plugins

(function() {

    var foo = function() { ... };

    var bar = (function() { ... }());

    myNamespace.plugins["MyPlugin"] = function() {

        ... do stuff
        ... bind to evevnts
    };

}());

Затем мы используем привязку для загрузки, которая была примерно такой:

(function() {

    var needed = function() {
         // Feature detection
    };

    var load = function() { ... };

    var getOptions = function() {
         // Call something on a page by page basis.
    };

    for (var plugin in pluginList) {
         if (needed(plugin)) {
               load(plugin, function() {
                    // get page specific options
                    var options = getOptions();
                    // run plugin
                    myNameSpace.plugins[plugin](options);
                    // If all have been loaded trigger ready handlers
                    if (pluginCurrentCount == pluginCount) {
                         readyTrigger();
                    }
               });
               pluginCount++;
         }
    }

    // start loading plugins after all have been counted
    load.startLoading();

    var readyTrigger = function() {
         // Run all ready handlers
    }

    // Implement your own DOM ready function to run when all plugins
    // have loaded.
    myNameSpace.ready = function(handler) {
         if (isReady) {
             handler();
         } else {
             readyList.push(handler);
         }

    };
}());

Это много пробелов и псевдокодов, но вы должны получить эту идею. Если это не obvouis чувствовать, чтобы подвергнуть сомнению его.

Тогда на странице у нас есть что-то вроде этого

<html>
<head>
  <script type="text/javascript">

    var pageSpecific = {
         "pluginName": {
              "cssClass": "foobar",
              "submitOnEnter": false,
              ...
         },
         ...
    };

  </script>
  <script src="bootstrapper.js" />
  ...
</head>
<body>
  ...
</body>
</html>

Ответ 2

Я работаю над шаблоном делегата pub/sub, который может работать для проблемы, описанной здесь. Я считаю, что цель состоит не в том, чтобы повторить код в нашем sub-objects, который мы уже писали в наших родительских объектах, если только этот код не является полностью специфичным для этого модуля.

В целом, я думаю, нам следует избегать этой идеи, что мы должны воспроизвести стиль программирования "классического наследования", который существует на других языках, и просто использовать возможности, которые у него есть у Java. "Наследование" может быть больше связано с вызовом свойств и методов в контексте выполнения (подобно тому, как мы используем .call() или .apply()), и не нужно создавать массивную базу кода, полную модулей, "расширяющих" другие модули, только чтобы в конечном итоге рассматривать их как полностью изолированные и отдельные объекты.

Я скоро поставлю это для использования в более крупном проекте и посмотрю, как это работает при работе с гораздо большим количеством кода (предупреждение: YES, я set свойства объектов __proto__):

// Global automatic reference counting
var referenceCount = 0;

// Core constructor (a factory function for objects)
function Class(obj) {

    // Represents `this` (the invoking object)
    var This = {};

    // Allow the inheritance of an object or object properties when `this` is defined
    for(var k in obj)
        This[k] = obj[k];

    // Basic `setter` method for `this`
    This.publish = function(obj) {
        for(var k in obj)
            this[k] = obj[k];
    };

    // Basic `unsetter` method for `this`
    This.unpublish = function(obj) {
        if(obj in this)
            delete this[obj];
        else
            for(var k in obj)
                if(k in this)
                    delete this[k];
    };

    // Allow `this` to subscribe to the updates of another object
    This.subscribe = function(obj) {
        this.__proto__ = obj;
    };

    // Allow `this` to unsubscribe from the updates of another object
    This.unsubscribe = function(obj) {
        this.subscribe({}.__proto__);
    };

    // Allow `this` to permanently consume a property from another object
    This.plagiarize = function(obj) {       
        var key = Object.keys(obj);
        var v, i = key.length;
        while(i--) {
            v = key[i];
            this[v] = obj[v][v];
        }
    };

    // Apply a unique identifier which corresponds with the global reference counter (and increment it)
    This.publish({ id: ++referenceCount });

    // return to `this` a newly constructed object
    return This;
}

Идея здесь в том, что вы пишете один API (или модуль, если хотите) для одного типа объектов. Например, ваш модуль может быть "View API", который содержит все методы, которые может когда-либо беспокоить объект вида. Вы пишете эти методы один раз и никогда больше. При написании кода в этих методах, как если бы модуль был любым объектом-вызовом (используя this или однажды определенный stand-in для this), вы можете избежать переписывания или переопределения свойств и методов позже в строке. Таким образом, этот API эффективно становится центральным delegate (или вы можете назвать его abstract class, если хотите) для любых будущих конкретных объектов, которые могут быть заинтересованы в его функциональности (например, представления). Мы уже пишем централизованные API на сервере, которые выступают в качестве делегатов для данных (большинство веб-API); почему мы не должны расширять эту же философию на стороне клиента, чтобы лучше организовать наши представления, клиентские модели и другие объекты?

Основное различие между этим шаблоном делегата и традиционными модульными шаблонами заключается в том, что с помощью этого шаблона вы, как правило, получаете один, несколько длинный модуль, который выглядит и выглядит как API с методами, которые могут давать очень разные результаты в зависимости от вызывая объект (и, следовательно, контекст исполнения), вместо того, чтобы иметь много полудлинных модулей, которые имеют сочетание логики, методов и уникальных свойств, которые в конечном итоге заставляют их повторять код друг друга. С помощью этого шаблона вы, вероятно, увидите намного меньше conditional логики, и вам больше не понадобится использовать var self = this - это то, что делает его абстрактным делегатом вместо конкретного модуля. Рамки, подобные Backbone, почти достигают этого, но в конечном итоге этого не делают. Backbone - это основа для "наследования объектов" и модульной организации кода - и это здорово - но я думаю, что наибольшая ценность, которую он добавляет, фактически состоит в том, что он получил людей, чтобы согласиться на "некоторый стандарт" (но я мог бы сказать то же самое о CoffeeScript, eww...).

Последний аспект этого шаблона затрагивает ваш первый пример. Включая базовые методы доступа и функциональность pub/sub в ядре, мы можем по-настоящему создавать объекты, которые полностью синхронизируются с родителями, когда мы хотим, чтобы они были, и иметь возможность разорвать свои отношения причудливо, если это необходимо - при необходимости, Я использовал prototypes для выполнения этого, но вы, вероятно, могли бы подумать и о другом способе (используя конструкторы). Я думаю, что это приводит к более верной версии pub/sub, где издатели ничего не знают о своих подписчиках, а подписчики знают только о издателях, когда и если они подписываются на них.

Подробнее об этом, у меня есть более подробный, документированный пример, в котором излагаются некоторые из возможных случаев использования в созданной мной структуре. Конечно, имена переменных, вероятно, должны измениться на имена, которые имеют больше смысла в вашем проекте - я использовал эти имена, чтобы передать, какова их цель. Здесь суть: https://gist.github.com/bennyschmidt/5069513

Мне интересно узнать больше о других модульных шаблонах дизайна, о которых вы все подумали.: >