Шаблон Javascript Revealing Module - раскрытие переменных инициализации после возврата функции

Я часто использую шаблон Javascript Revealing Module, и мне нравится четкое разделение, которое он дает между общедоступным интерфейсом и внутренними компонентами. Однако я продолжаю сталкиваться с ситуацией, которая заставляет меня задаться вопросом, верен ли мой общий шаблон использования или мне следует использовать какой-то вариант шаблона.

Проблема заключается в том, что когда что-то, переданное в функцию инициализации модуля и сохраненное в частном порядке для внутреннего использования, также должно быть открыто для всех, либо в выражении привязки Knockout, либо в каком-либо другом модуле. Оператор return модуля выполняется немедленно, а через некоторое время вызывается функция инициализации, обычно передавая некоторые динамические параметры, такие как URL-адреса Ajax или необработанный JSON, отображаемый в блоке скрипта в представлении Razor. Поскольку оператор return модуля просто возвращает копию приватной переменной, а не ссылку, установка этой приватной переменной в функции инициализации не может изменить то, что уже было возвращено.

var productsModule = function() {

var urls;

var init = function(ajaxUrls) {
    urls = ajaxUrls;
};

return {
    init: init,
    urls: urls,
    getUrls: function() { return urls; }
};

}();

var customersModule = function() {

var doSomethingWithProductsModule = function() {
    alert(productsModule.urls); // undefined
    alert(productsModule.getUrls()); // object   
} ;       

return {
    doSomethingWithProductsModule: doSomethingWithProductsModule
};

}();

var urls = {
getProduct: '/Product/'
};

productsModule.init(urls);

customersModule.doSomethingWithProductsModule();

Мой обходной путь — просто обернуть такие объекты, как «urls», в функцию, а затем получить к ним доступ через productsModule.getUrls(). Однако это становится очень запутанным, особенно если переменная является наблюдаемой Knockout, которая сама является функцией, и, следовательно, для ее оценки мне нужно использовать двойные скобки, такие как productsModule.getMyObservable()().

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


person Tom W Hall    schedule 26.10.2012    source источник


Ответы (3)


Базовые типы передаются по значению, а объекты передаются по ссылке; вы можете использовать это так, чтобы вместо перезаписи urls в productsModule вы просто обновляли его. Таким образом, ссылка, возвращенная при первоначальном вызове модуля, остается актуальной. Я обновил ваш код, чтобы показать, что я имею в виду.

var productsModule = function() {

var urls = {};

var init = function(ajaxUrls) {
    // Merge properties into the original object instead; more robust approach
    // may be needed
    for ( name in ajaxUrls ) {
        if (ajaxUrls.hasOwnProperty(name)) {
            urls[name] = ajaxUrls[name];
        }
    }
};

return {
    init: init,
    urls: urls,
    getUrls: function() { return urls; }
};

}();

var customersModule = function() {

var doSomethingWithProductsModule = function() {
    alert(productsModule.urls); // undefined
    alert(productsModule.getUrls()); // object
} ;   

return {
    doSomethingWithProductsModule: doSomethingWithProductsModule
};

}();

var urls = {
    getProduct: '/Product/'
};

productsModule.init(urls);

customersModule.doSomethingWithProductsModule();
person El Yobo    schedule 26.10.2012
comment
объекты передаются по ссылке, не лучше ли сказать все передаются по значению, но значение объекта является ссылкой? - person alex; 26.10.2012
comment
Ой, теперь у меня болит голова ;) Я не уверен, что это на самом деле говорит о чем-то другом. - person El Yobo; 26.10.2012
comment
Если бы это была ссылка, ожидали бы вы, что этот jsfiddle выведет "not a value"? Я не уверен на 100%, но я уверен, что кто-нибудь заглянет и даст определенный ответ. - person alex; 26.10.2012
comment
Этот вопрос превосходен в этом отношении: stackoverflow.com/questions/518000/ переданный элемент передается по значению. Но элемент, который передается по значению, сам по себе является ссылкой. - person Tom W Hall; 26.10.2012
comment
Спасибо @TomHall. Такое поведение довольно распространено; PHP делает то же самое (но имеет третий вариант явной передачи по ссылке, который решит эту проблему). Это обычно описывается как передача объектов по ссылке, но, по-видимому, это тоже не совсем так; php.net/manual/en/language.oop5.references.php. - person El Yobo; 26.10.2012

Хотя мне не совсем нравится идея перебирать все возможные уровни моих объектов, чтобы объединить их таким образом, ответ Эль Йобо заставил меня задуматься о том, чтобы сделать результат самой функции модуля локальной переменной, свойства которой я мог бы обновить .

var productsModule = function() {

var urls;

var init = function(ajaxUrls) {
urls = ajaxUrls;
result.urls = urls;
};

var result = {
init: init,
urls: urls
};

return result;

}();


// Before init
alert(productsModule.urls); // undefined

var urls = {
getProduct: '/Product/'
};

productsModule.init(urls);

alert(productsModule.urls.getProduct); // /Product/
person Tom W Hall    schedule 26.10.2012
comment
Если вы собираетесь передавать только простые объекты, то накладные расходы на слияние минимальны, но так кажется лучше. - person El Yobo; 26.10.2012
comment
Нахально с моей стороны принять мой собственный ответ, но спасибо, что указали мне правильное направление. - person Tom W Hall; 29.10.2012
comment
@TomHall: Принятие собственного ответа - это нормально. Написание вопроса в ответе - нет. - person Lightness Races in Orbit; 19.08.2014

Почему бы вам не сделать URL наблюдаемым свойством?

Посмотрите на мой пример:

http://jsfiddle.net/Razaz/zkXYC/1/

var productsModule = function() {

    var urls=ko.observable();

    var init = function(ajaxUrls) {
        urls(ajaxUrls);
    };

    return {
        init: init,
        urls: urls,
        getUrls: function() { return urls(); }
    };

}();

var customersModule = function() {

    var doSomethingWithProductsModule = function() {
        alert(productsModule.urls()); // undefined
        alert(productsModule.getUrls()); // object   
    };       

    return {
        doSomethingWithProductsModule: doSomethingWithProductsModule
    };

}();

var urls = {
    getProduct: '/Product/'
};

productsModule.init(urls);

customersModule.doSomethingWithProductsModule();​

Привет.

person Julián Yuste    schedule 26.10.2012
comment
В мире Knockout.js это, вероятно, разумный вариант для некоторых свойств, хотя это немного похоже на ненужные накладные расходы (подписки) и скобки только для того, чтобы обойти неудобный поток RMP. Часто мой модуль будет отображать тяжелую модель представления верхнего уровня, которую я не хочу наблюдать за собой, только ее свойства и свойства ее дочерних моделей представлений. Но тем не менее хорошая мысль. - person Tom W Hall; 27.10.2012