Шаблон модуля - как разделить код для одного модуля на разные JS-файлы?

для шаблона модуля я делаю что-то вроде:

(function(namespace) {
    // tons of code
    // blabla
})(window.myGlobalNamespace);

как разделить код? Я могу придумать несколько способов, как использовать иерархию пространств имен, или расширить объект за пределами window.myGlobalNamespace.additionalFunc = function () {//blabla}. Каковы другие пути? Каковы плюсы и минусы? Какая из них считается лучшей практикой?

оба ответа предлагают RequireJS. Не могли бы вы объяснить, как RequireJS может решить эти проблемы:

первый.js:

(function(context) {
    var parentPrivate = 'parentPrivate';
})(window.myGlobalNamespace);

второй.js:

(function(context) {
    this.childFunction = console.log('trying to access parent private field: ' + parentPriavte);
}(window.myGlobalNamespace.subNamspace);

main.js:

window.myGlobalNamespace.subNamspace.childFunction(); // doesn't work

и люди могут сделать

window.myGlobalNamespace.subNamspace.childFunction = function() {alert("a new function");}

изменить поведение моего кода!

здесь есть две проблемы:

  1. у нас не может быть поля, доступного ребенку, но не для внешней общественности (т. е. защищенного). Есть ли способ достичь этого?

  2. если нет, то есть, если мы wanteparentPrivate чтобы быть доступными, мы должны сделать это публичным. Затем пользователь сможет изменить его!

что больше, все общественные функции можно изменить и заменить. Я не хочу, чтобы это произошло.

Я не вижу, как RequireJS решает эти проблемы. Кто-нибудь может пролить свет?

4 ответов


есть только 2 способа получить JavaScript в HTML:

  1. Inline - <script> some JavaScript </script>
  2. ссылке - <script src='main.js'></script>

Я знаю, что это очевидно, но нам нужен общий язык, что будет дальше. ;)

JavaScript не имеет возможности "импортировать"другие файлы JavaScript в себя. Весь "импорт" выполняется в формате HTML. Вы можете сделать это несколько пути:

  • ссылке каждый по отдельности в HMTL
  • динамически ссылке их через некоторый JavaScript

    var script = document.createElement("script");
    script.src = "all.js";
    document.documentElement.firstChild.appendChild(script);
    
  • библиотека как RequireJS. Помощью RequireJS использует API определения асинхронного модуля (AMD). Это механизм JavaScript для определения модулей таким образом, что модуль и его зависимости могут быть асинхронно загружены.

это импорт, чтобы рассмотреть причины разделения JavaScript на отдельные файлы.

  • ремонтопригодность - становится легче работать над одной частью за раз
  • читабельности - если все находится в одном большом файле, очень трудно увидеть, что есть что
  • разделение труда - легче иметь несколько разработчиков, работающих над несколькими файлами вместо одного большого один
  • использовать - все ваши функции могут быть разбиты на высоко-когезивный модули

отдельные файлы JavaScript НЕ сделать вещи частная, закрытие делает вещи частными.

Теперь рассмотрим в конце дня, когда все готово к производству лучшее, что вы могли бы сделать, это оптимизация ваш JavaScript, объединив все это в один файл, чтобы пользователь только есть один файл для загрузки.


при работе с частными переменными в JavaScript вы в какой-то момент захотите получить к ним доступ.

  • общественные

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

http://requirejs.org

хорошее чтение: http://net.tutsplus.com/tutorials/javascript-ajax/principles-of-maintainable-javascript/


защищенные переменные в javascript могут быть достигнуты путем передачи защищенных переменных в зависимости. Подкласс должен быть создан внутри родителя, поскольку только там он имеет доступ к защищенным переменным. пример jsFiddle

App = window.App || {};
App.ParentClass = (function(){

   var protectedState = {
      protectedVar: 'wow such protection'
   }

   return{
      SubClassInstance: App.SubClass(protectedState), //make the subclass accessible from outside
   }
})(); //closure gives us privacy

подкласс.js

App = window.App || {};
App.SubClass = function(protectedState){

    return {
        logProtectedVar : function(){
            //can access protectedState of parent
            console.log(protectedState.protectedVar);
        }
    }
}// I require protected state to work

main.js

// protected variable is accessible from parent and subclass and nowhere else

App.ParentClass.SubClassInstance.logProtectedVar();
// 'wow such protection' 

Примечание: как упоминал Чарльз У., этот шаблон работает только тогда, когда protectedState - это объект. Если бы это была строка/int, это было бы передано по значению, и изменения, внесенные в подкласс, не будут видны из копии родителей.


модульность (разделение кода) не совпадает с защитой данных (скрытие данных).

RequireJS решает проблему модульности, а не проблему защиты данных. Или, другими словами... Какие бы проблемы ни возникали при попытке защитить данные и какие бы решения ни существовали для защиты данных, эти проблемы и решения одинаковы с или без RequireJS.

RequireJS реализует все механики для указания зависимостей между модулями, чтобы загрузите эти зависимости только по мере необходимости, чтобы избежать перезагрузки вещей, которые уже были загружены, избежать загрузки вещей, которые не требуются вообще, быстро изменить местоположение модулей, имеют избыточность и т. д.

после развертывания, если кто-то находит RequireJS как-то слишком тяжелым, есть миндаль библиотека, которую можно использовать вместо этого.

у нас не может быть поля, доступного ребенку, но не для внешней общественности (т. е. защищенного). Есть ли способ достичь этого?

если вы хотите модульность (т. е. вы хотите, чтобы ребенок кодировался отдельно от родителя), я не считаю, что это возможно в JavaScript. Было бы возможно, чтобы ребенок и родитель работали в то же закрытие но тогда этого не было бы модульная. Это верно с RequireJS или без него.

если нет, то есть, если мы хотим, чтобы мы были доступны, нам нужно сделать это общественный. Затем пользователь сможет изменить его!

если вы хотите предотвратить присваивание parentPrivate, вы можете использовать Object.freeze() в пространстве имен, содержащем parentPrivate.

, Я не знаю, насколько хорошо он поддерживается различными браузерами. И если то, что в parentPrivate сам по себе является объектом, а не примитивным значением, его также необходимо заморозить, если вы не хотите, чтобы он был изменен клиентами вашего кода. И если объект заморожен, он заморожен. для всех так модуль что принадлежит объект не получает специального обращения для его изменения. А замерзания нет скрыть ничего.

или вы можете использовать сеттеры и геттеры, как в этом примере RequireJS:

define(function () {
    'use strict';

    var writable = "initial value";

    var namespace = {
        get unwritable() { return writable; },
        doSomething: function () { writable = "changed value"; }

    };

    return namespace;

});

если модуль импортируется как parent, потом parent.unwritable не может быть записан, но сам модуль все еще может изменить значение, возвращаемое путем записи в writable. Обратите внимание, что если возвращаемое значение возвращаемое геттером является объект, а не примитивное значение, этот объект can быть изменен абонентом.

опять же, это верно, используете ли вы RequireJS. Решения те же, проблемы те же и т. д.