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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5608685/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:02:21  来源:igfitidea点击:

Using RequireJS, how do I pass in global objects or singletons around?

javascriptsingletonrequirejs

提问by egervari

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?

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

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."

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

回答by Domenic

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

回答by Raynos

Just provide an API for your singleton as you would.

只需像您一样为您的单身人士提供一个 API。

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.

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

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

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

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

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

});

_.oncefrom underscore.

_.once从下划线。

回答by Domenic

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:

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

Function.prototype.bind不会出现在所有浏览器中,因此您需要像Mozilla 提供的那样包含一个 polyfill 。

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 notifyand listenmethods to everyone who uses your module, and those always refer to the same privatelistenerMapvariable, 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.

由于您向使用您的模块的每个人公开相同的notifylisten方法,并且这些方法始终引用相同的私有listenerMap变量,因此这应该可以满足您的需求。它还消除了对 的需求Function.prototype.bind,并消除了消息服务本身与强制单例使用它的模块之间相当不必要的区别。

回答by Benny Bottema

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

回答by Eric Bronnimann

As a variation of Domenic's answer, you can use the 'exports' magic moduleto 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.

作为 Domenic 答案的变体,您可以使用“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);
});


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.
  • 将“exports”声明为定义数组中的第一个依赖项。
  • 在 JavaScript 文件之后命名函数中的参数。

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.setBarOnFooto bar.setFooBaris 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.

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

回答by Sanchitos

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.

由于不同的原因,我需要调用一个位于 requirejs 模块上的函数,但是触发该调用的点击是不需要的。

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

我解决这个问题的方法是创建一个写在 window 对象上的 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();

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.

这样,如果任何超出 require 范围的代码(我知道它不应该这样)可以调用 this require 的函数,该函数会覆盖 window 对象。

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

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