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

发布于 2024-10-31 06:27:17 字数 125 浏览 5 评论 0原文

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

基本上我想做的是说,“如果未加载此依赖项...则加载它。否则,使用已加载的相同实例并传递该实例。”

Let's say I am writing code at the main page level and 2 dependencies require the same instance of an object and also state that as a dependency. What is the appropriate way to go about this?

Basically what I want to do is say, "If this dependency isn't loaded... then load it. Otherwise, use the same instance that was already loaded and just pass that one."

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

山色无中 2024-11-07 06:27:17

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

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

You would make that a module-level variable. For example,

// 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");
};
溺渁∝ 2024-11-07 06:27:17

只需按照您的意愿为您的单例提供一个 API 即可。

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

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

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

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

});

_.once 来自下划线。

Just provide an API for your singleton as you would.

And make sure its lazily loaded. The easiest way is to use an abstraction library like underscore that supplies cross browser helpers. Other options are ES5 Object.defineProperty or custom getter/setters.

In this case _.once ensures that constructor's result is cached after the first call, it basically lazy loads it.

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

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

});

_.once from underscore.

疯狂的代价 2024-11-07 06:27:17

将 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 不会存在于所有浏览器中,因此您需要包含一个类似于 Mozilla 的 polyfill提供

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

// 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 的需要,并消除了消息传递服务本身和强制单例使用它的模块之间相当不必要的区别。

Combining Raynos's concerns about encapsulation with the OP's clarification that he wants to expose a couple of methods on a messaging service, this is I think the right way to go about it:

// 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 will not be present in all browsers, so you would need to include a polyfill like the one Mozilla provides.

An alternate (and in my opinion probably better) approach would be to make the messaging service object itself a module. This would look something like

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

Since you expose the same notify and listen methods to everyone who uses your module, and those always refer to the same private listenerMap variable, this should do what you want. It also obviates the need for Function.prototype.bind, and gets rid of the rather-unnecessary distinction between the messaging service itself and the module which enforces singleton usage of it.

长梦不多时 2024-11-07 06:27:17

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

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

Here's a version where the module itself is the shared variable instead of a variable within that module.

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
绳情 2024-11-07 06:27:17

作为多梅尼克答案的变体,您可以使用 '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”声明为定义数组中的第一个依赖项。
  • 以 JavaScript 文件命名函数中的参数。

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

As a variation of Domenic's answer, you can use the 'exports' magic module to automatically generate a reference for the module -- "Properties added to the exports object will be on the public interface of the module, no need to return any value." This avoids having to call the getTheFoo() function to obtain a reference.

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

To address the comment below, I personally have found the above convention to be useful. Your mileage may vary, but feel free to adopt the convention if you think it is useful. The convention boils down to these two rules:

  • Declare 'exports' as the first dependency in the define array.
  • Name the parameter in the function after the JavaScript file.

Using the name of file, e.g. for foo.js name the variable 'foo', increases the readability of the code as most developers will define 'foo' as the parameter for the foo.js dependency. When scanning the code or using grep, it is easy to find all references to 'foo' use both inside and outside the module and it makes it easy to pick out what the module is exposing to the public. For example, renaming bar.setBarOnFoo to bar.setFooBar is much easier if the declaration in the bar.js module mirrors the usage in other files. A simple search and replace of bar.setBarOnFoo to bar.setFooBar across all files will accomplish the task.

多情出卖 2024-11-07 06:27:17

我处于这种情况:

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

我解决这个问题的方法是创建一个 requirejs modure 来重写 window 对象。

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

这样,如果任何超出 require 范围的代码(我知道不应该是这种方式)都可以调用此 require 的函数来重写 window 对象。

我真的知道这不是一个“优雅”的解决方案,但在紧急情况下它可能会帮助你。

I was in this scenario:

For different reasons I needed to call a function that was on a requirejs module, but the click that fired that call was out of require.

The way I fixed this was creating a requirejs modure that writes over the window object.

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

This way if any piece of code that is out of the require spectrum (I know it shouldn't be this way) can call functions of this require that writes over the window object.

I really know this is not an "elegant" solution, but it may help you in case of an emergency.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文