假设我正在主页面级别编写代码,并且 2 个依赖项需要对象的相同实例,并将其声明为依赖项。解决此问题的适当方法是什么?
基本上我想做的是说,“如果这个依赖项没有加载......然后加载它。否则,使用已经加载的相同实例并传递那个。”
假设我正在主页面级别编写代码,并且 2 个依赖项需要对象的相同实例,并将其声明为依赖项。解决此问题的适当方法是什么?
基本上我想做的是说,“如果这个依赖项没有加载......然后加载它。否则,使用已经加载的相同实例并传递那个。”
您可以将其设为module级变量。例如,
// 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");
};
只需像您一样为您的单身人士提供一个 API。
并确保其延迟加载。最简单的方法是使用像 underscore 这样提供跨浏览器助手的抽象库。其他选项是 ES5 Object.defineProperty 或自定义 getter/setter。
在这种情况下,_.once
确保构造函数的结果在第一次调用后被缓存,它基本上是延迟加载它。
define(function() {
var constructor = _.once(function() {
...
});
return {
doStuffWithSingleton: function() {
constructor().doStuff();
}
};
});
_.once
从下划线。
将 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 。
另一种(在我看来可能更好)的方法是使消息服务对象本身成为一个module。这看起来像
// 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
};
});
由于您向使用您的module的每个人公开相同的notify
和listen
方法,并且这些方法始终引用相同的私有 listenerMap
变量,因此这应该可以满足您的需求。它还消除了对 的需求Function.prototype.bind
,并消除了消息服务本身和强制单例使用它的module之间相当不必要的区别。
这是一个module本身是共享变量而不是该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
作为 Domenic 答案的变体,您可以使用“exports”魔法module自动生成module的引用——“添加到导出对象的属性将位于module的公共接口上,无需返回任何值。 ” 这避免了必须调用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);
});
为了解决下面的评论,我个人发现上述约定很有用。您的里程可能会有所不同,但如果您认为它有用,请随时采用该约定。该公约归结为以下两条规则:
使用文件名,例如将 foo.js 命名为变量 'foo',可以提高代码的可读性,因为大多数开发人员将定义 'foo' 作为 foo.js 依赖项的参数。扫描代码或使用 grep 时,很容易找到module内部和外部对“foo”使用的所有引用,并且可以轻松挑选出module向公众公开的内容。例如,重命名bar.setBarOnFoo
到bar.setFooBar
是容易得多,如果在bar.js声明module镜子在其他文件中使用。在所有文件中简单地搜索和替换 bar.setBarOnFoo 到 bar.setFooBar 即可完成任务。