使用RequireJS,如何传递全局对象或单例?

时间:2011-04-09 23:41:01

标签: javascript singleton requirejs

假设我在主页面级别编写代码,并且2个依赖项需要对象的相同实例,并且还将其声明为依赖项。有什么方法可以解决这个问题?

基本上我想做的就是说,“如果没有加载这个依赖关系......那么加载它。否则,使用已经加载的同一个实例,然后传递那个。”

6 个答案:

答案 0 :(得分:58)

您可以将其设为模块级变量。例如,

// 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");
};

答案 1 :(得分:8)

只需为您的单身人士提供API。

并确保其懒洋洋地加载。最简单的方法是使用像下划线那样提供跨浏览器帮助程序的抽象库。其他选项是ES5 Object.defineProperty或自定义getter / setter。

在这种情况下_.once确保构造函数的结果在第一次调用后被缓存,它基本上是延迟加载它。

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

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

});
来自下划线的

_.once

答案 2 :(得分:6)

将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不会出现在所有浏览器中,因此您需要添加the one Mozilla provides之类的填充。

备用(在我看来可能更好)的方法是使消息传递服务对象本身成为一个模块。这看起来像

// 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
    };
});

由于您向使用您的模块的每个人公开了相同的notifylisten方法,并且这些方法始终引用相同的private listenerMap变量,这应该做什么你要。它还消除了Function.prototype.bind的需要,并消除了消息传递服务本身与强制单独使用它的模块之间相当不必要的区别。

答案 3 :(得分:1)

这是一个版本,其中模块本身是共享变量而不是该模块中的变量。

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

答案 4 :(得分:0)

作为Domenic答案的变体,您可以使用'exports' magic module自动生成模块的引用 - “添加到exports对象的属性将位于模块的公共接口上,无需返回任何价值。“这样就不必调用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);
});

为了解决以下评论,我个人发现上述惯例很有用。您的里程可能会有所不同,但如果您认为它有用,请随意采用该惯例。惯例归结为这两个规则:

  • 将'exports'声明为define数组中的第一个依赖项。
  • 在JavaScript文件后面的函数中命名参数。

使用文件名,例如对于foo.js命名变量'foo',增加了代码的可读性,因为大多数开发人员将'foo'定义为foo.js依赖项的参数。在扫描代码或使用grep时,很容易在模块内部和外部找到对“foo”的所有引用,并且可以很容易地找出模块向公众公开的内容。例如,如果bar.js模块中的声明镜像其他文件中的用法,则将bar.setBarOnFoo重命名为bar.setFooBar要容易得多。在所有文件中简单搜索并将bar.setBarOnFoo替换为bar.setFooBar将完成任务。

答案 5 :(得分:-1)

我在这种情况下:

出于不同的原因,我需要调用requirejs模块上的函数,但触发该调用的单击不需要。

我修复此问题的方法是创建一个在窗口对象上写入的requirejs modure。

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();

这样,如果任何超出要求频谱的代码(我知道它不应该是这种方式)可以调用此函数来写入窗口对象。

我真的知道这不是一个优雅的"解决方案,但它可以帮助您在紧急情况下。