35

我可以拉伸多少 RequireJS 来为我的应用程序提供依赖注入?例如,假设我有一个想要成为单身人士的模型。不是自我执行的 getInstance() 类型的单例中的单例,而是上下文强制的单例(每个“上下文”一个实例)。我想做类似...

require(['mymodel'], function(mymodel) {
   ...
}

并让 mymodel 成为 MyModel 类的一个实例。如果我要在多个模块中执行此操作,我希望 mymodel 是相同的共享实例。

我通过制作这样的 mymodel 模块成功地完成了这项工作:

define(function() {
    var MyModel = function() {
        this.value = 10;
    }
    return new MyModel();
});

这种类型的用法是预期的和常见的,还是我在滥用 RequireJS?有没有更合适的方法可以使用 RequireJS 执行依赖注入?谢谢你的帮助。仍在努力掌握这一点。

4

3 回答 3

60

这实际上不是依赖注入,而是服务位置:您的其他模块通过字符串“键”请求“类”并取回“服务定位器”(在本例中为 RequireJS)已连接到的实例为他们提供。

依赖注入将涉及返回MyModel构造函数,即return MyModel,然后在中央组合根中将的实例MyModel注入其他实例。我在这里整理了一个如何工作的示例:https ://gist.github.com/1274607 (也在下面引用)

这样,组合根决定是否MyModel为每个需要它的类分发一个实例(即使其成为单例作用域)或新实例(实例作用域),或者介于两者之间。该逻辑既不属于 MyModel 的定义,也不属于请求它的实例的类。

(旁注:虽然我没有使用过,wire.js是一个成熟的 JavaScript 依赖注入容器,看起来很酷。)


尽管您正在做的事情似乎有点迂回,但您不一定会像您一样使用 RequireJS 来滥用它,即声明一个类而不是返回它的一个新实例。为什么不只做以下事情?

define(function () {
    var value = 10;

    return {
        doStuff: function () {
            alert(value);
        }
    };
});

您可能缺少的类比是模块等效于大多数其他语言中的“命名空间”,尽管您可以将函数和值附加到命名空间。(所以更像 Python 而不是 Java 或 C#。)它们不等同于类,尽管正如您所展示的,您可以使模块的导出等于给定类实例的导出。

因此,您可以通过将函数和值直接附加到模块来创建单例,但这有点像使用静态类创建单例:它非常不灵活,通常不是最佳实践。但是,大多数人确实将他们的模块视为“静态类”,因为正确地构建依赖注入系统需要从一开始就进行大量思考,这在 JavaScript 中并不是真正的规范。


这是https://gist.github.com/1274607内联:

// EntryPoint.js
define(function () {
    return function EntryPoint(model1, model2) {
        // stuff
    };
});

// Model1.js
define(function () {
    return function Model1() {
        // stuff
    };
});

// Model2.js
define(function () {
    return function Model2(helper) {
        // stuff
    };
});

// Helper.js
define(function () {
    return function Helper() {
        // stuff
    };
});

// composition root, probably your main module
define(function (require) {
    var EntryPoint = require("./EntryPoint");
    var Model1 = require("./Model1");
    var Model2 = require("./Model2");
    var Helper = require("./Helper");

    var entryPoint = new EntryPoint(new Model1(), new Model2(new Helper()));
    entryPoint.start();
});
于 2011-10-10T04:15:47.067 回答
3

如果你对 DI / IOC 很认真,你可能会对wire.js 感兴趣:https ://github.com/cujojs/wire

我们使用服务重定位(如 Domenic 描述的,但使用 curl.js 而不是 RequireJS)和 DI(使用 wire.js)的组合。在测试工具中使用模拟对象时,服务重定位非常方便。DI 似乎是大多数其他用例的最佳选择。

于 2011-10-20T13:01:24.890 回答
2

不是自我执行的 getInstance() 类型的单例中的单例,而是上下文强制的单例(每个“上下文”一个实例)。

我只推荐它用于静态对象。将静态对象作为您在 require/define 块中使用的模块加载是非常好的。然后创建一个只有静态属性和函数的类。然后,您就有了数学对象的等价物,它具有诸如 PI、E、SQRT 之类的常量以及诸如 round()、random()、max()、min() 之类的函数。非常适合创建可以随时注入的实用程序类。

而不是这个:

define(function() {
    var MyModel = function() {
        this.value = 10;
    }
    return new MyModel();
});

它创建一个实例,使用静态对象的模式(其中值始终与对象永远不会被实例化相同):

define(function() {
    return {
       value: 10
    };
});

或者

define(function() {
    var CONSTANT = 10;
    return {
       value: CONSTANT
    };
});

如果要传递一个实例(使用具有 return new MyModel(); 的模块的结果),那么在初始化函数中,传递一个捕获当前状态/上下文的变量或传递包含有关信息的对象您的模块需要了解的状态/上下文。

于 2012-09-11T04:02:27.643 回答