Подтвердить что ты не робот

С помощью RequireJS, как мне проходить глобальные объекты или синглтоны?

Скажем, я пишу код на уровне главной страницы, а 2 зависимости требуют одного и того же экземпляра объекта, а также указывают, что в качестве зависимости. Каков подходящий способ сделать это?

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

4b9b3361

Ответ 1

Вы сделали бы эту переменную уровня модуля. Например,

// In foo.js
define(function () {
    var theFoo = {};

    return {
        getTheFoo: function () { return theFoo; }
    };
});

// In bar.js
define(["./foo"], function (foo) {
    var theFoo = foo.getTheFoo(); // save in convenience variable

    return {
        setBarOnFoo: function () { theFoo.bar = "hello"; }
    };
}

// In baz.js
define(["./foo"], function (foo) {
    // Or use directly.
    return {
        setBazOnFoo: function () { foo.getTheFoo().baz = "goodbye"; }
    };
}

// In any other file
define(["./foo", "./bar", "./baz"], function (foo, bar, baz) {
    bar.setBarOnFoo();
    baz.setBazOnFoo();

    assert(foo.getTheFoo().bar === "hello");
    assert(foo.getTheFoo().baz === "goodbye");
};

Ответ 2

Просто предоставляйте API для своего синглтона, как и вы.

И убедитесь, что он лениво загружен. Самый простой способ - использовать библиотеку абстракции, такую ​​как подчеркивание, которая предоставляет кросс-браузерные помощники. Другие варианты: ES5 Object.defineProperty или пользовательские getter/seters.

В этом случае _.once гарантирует, что результат конструктора будет кэширован после первого вызова, он в основном ленивый загружает его.

define(function() {
    var constructor = _.once(function() { 
        ...
    });

    return {
        doStuffWithSingleton: function() {
            constructor().doStuff();
        }
    };

});

_.once от подчеркивания.

Ответ 3

Объединив опасения Raynos относительно инкапсуляции с разъяснением OP, что он хочет разоблачить несколько методов в службе обмена сообщениями, я думаю, что это правильный способ:

// In messagingServiceSingleton.js
define(function () {
    var messagingService = new MessagingService();

    return {
        notify: messagingService.listen.bind(messagingService),
        listen: messagingService.notify.bind(messagingService)
    };
});

// In bar.js
define(["./messagingServiceSingleton"], function (messagingServiceSingleton) {
    messagingServiceSingleton.listen(/* whatever */);
}

// In baz.js
define(["./messagingServiceSingleton"], function (messagingServiceSingleton) {
    messagingServiceSingleton.notify(/* whatever */);
}

Function.prototype.bind не будет присутствовать во всех браузерах, поэтому вам нужно будет включить polyfill как тот, который предоставляет Mozilla.

Альтернативный (и, на мой взгляд, лучше) подход должен заключаться в том, чтобы сделать объект службы обмена сообщениями сам модулем. Это выглядело бы как

// In messagingService.js
define(function () {
    var listenerMap = {};

    function listen(/* params */) {
        // Modify listenerMap as appropriate according to params.
    }
    function notify(/* params */) {
        // Use listenerMap as appropriate according to params.
    }

    return {
        notify: notify
        listen: listen
    };
});

Поскольку вы показываете те же методы notify и listen всем, кто использует ваш модуль, и они всегда ссылаются на те же private listenerMap, это должно делать то, что вы хотите. Он также устраняет необходимость в Function.prototype.bind и избавляется от довольно ненужного различия между самой службой обмена сообщениями и модулем, который обеспечивает использование одного синтаксиса.

Ответ 4

Здесь версия, где сам модуль является общей переменной вместо переменной внутри этого модуля.

define('foo', [], {bar: "this text will be overwritten"});

define('bar', ["foo"], function (foo) {
    return {
        setBarOnFoo: function () { foo.bar = "hello"; }
    };
});

define('baz', ["foo"], function (foo) {
    return {
        setBazOnFoo: function () { foo.baz = "goodbye"; }
    };
});

require(["foo", "bar", "baz"], function (foo, bar, baz) {
    bar.setBarOnFoo();
    baz.setBazOnFoo();

    $('#results').append(foo.bar + ' ' + foo.baz);
});​​​

// reads: hello goodbye

Ответ 5

В качестве вариации Доменического ответа вы можете использовать "магический модуль экспорта" , чтобы автоматически генерировать ссылку для модуля - "Свойства добавленный в объект экспорта, будет находиться в открытом интерфейсе модуля, не нужно возвращать какое-либо значение". Это позволяет избежать вызова функции getTheFoo() для получения ссылки.

// In foo.js
define(['exports'], function (foo) {
   foo.thereCanBeOnlyOne = true; 
});

// In bar.js
define(["exports", "./foo"], function (bar, foo) {
  bar.setBarOnFoo = function () { foo.bar = "hello"; };
});

// in baz.js
define(["exports", "./foo"], function (baz, foo) {
  baz.setBazOnFoo = function () { foo.baz = "goodbye"; };
});

// In any other file
define(["./foo", "./bar", "./baz"], function (foo, bar, baz) {
  bar.setBarOnFoo();
  baz.setBazOnFoo();

  assert(foo.bar === "hello");
  assert(foo.baz === "goodbye");
  assert(foo.thereCanBeOnlyeOne);
});

Чтобы ответить на приведенный ниже комментарий, я лично нашел, что вышеуказанное соглашение полезно. Ваш пробег может отличаться, но не стесняйтесь принять конвенцию, если считаете, что это полезно. Соглашение сводится к следующим двум правилам:

  • Объявить "экспорт" в качестве первой зависимости в массиве define.
  • Назовите параметр в функции после файла JavaScript.

Использование имени файла, например. для foo.js назовите переменную 'foo', повышает читабельность кода, поскольку большинство разработчиков определит 'foo' как параметр для зависимости foo.js. При сканировании кода или использовании grep легко найти все ссылки на "foo" как внутри, так и вне модуля, и это упрощает выбор того, что модуль предоставляет общественности. Например, переименование bar.setBarOnFoo в bar.setFooBar намного проще, если объявление в модуле bar.js отражает использование в других файлах. Простой поиск и замена bar.setBarOnFoo на bar.setFooBar по всем файлам выполнит задачу.

Ответ 6

Я был в этом сценарии:

По разным причинам мне нужно было вызвать функцию, которая была в модуле requirejs, но щелчок, который вызывал этот вызов, не требовал.

Способ, которым я исправил это, - это создать мотив requirejs, который записывает поверх объекта окна.

define("one", [], function() {
    window.popupManager = (function () {
            console.log ('aca');

        var popUpManager = function () {
            self = this;

            self.CallMe = function ()
            {
                alert ('someone calls');
            };
        };
        return new popUpManager();
    })();
});
require(['one']);

window.popupManager.CallMe();

Таким образом, если какой-либо фрагмент кода, который выходит из требуемого спектра (я знаю, что этого не может быть так), может вызывать функции этого, которые требуют записи над объектом окна.

Я действительно знаю, что это не "элегантное" решение, но оно может помочь вам в случае чрезвычайной ситуации.