假设我正在编写主页面级别的代码,有两个依赖项需要同一个对象实例,并将其作为依赖项。应该如何处理?
基本上我想说,“如果这个依赖项没有加载...那么加载它。否则,使用已经加载的相同实例并传递给它。”
假设我正在编写主页面级别的代码,有两个依赖项需要同一个对象实例,并将其作为依赖项。应该如何处理?
基本上我想说,“如果这个依赖项没有加载...那么加载它。否则,使用已经加载的相同实例并传递给它。”
你可以将它作为模块级变量。例如:
// 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");
};
_.once
确保构造函数的结果在第一次调用后被缓存,它基本上是惰性加载它。define(function() {
var constructor = _.once(function() {
...
});
return {
doStuffWithSingleton: function() {
constructor().doStuff();
}
};
});
_.once
,它会起作用。如果你不能使用 ES5,我实际上建议使用 _.once
或自己的惰性加载 getters。 - Raynosbaz
模块中,您仍然可以执行foo.lazyloaded.bar = "not me"
; 使用构造函数而不是theFoo = {}
并不能解决任何问题,尽管如果OP确实需要以那种方式构建对象,它当然更强大。 - Domenic结合 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提供的填充程序。
另一种(我个人认为可能更好的)方法是将消息服务对象本身作为模块。这将看起来像:
// 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
};
});
Function.prototype.bind
的需要,并消除了消息服务本身与强制其单例使用的模块之间的相当不必要的区别。obj.f.bind(obj)
而不是 function() { obj.f(); }
。另外,像 {_.bindAll(obj)
](http://documentcloud.github.com/underscore/#bindAll) 这样的抽象对于这些情况非常有用。 - Raynosdefine('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
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时,很容易找到所有内外模块中使用“foo”的引用,并且它使得轻松地挑选出模块向公众公开的内容。例如,如果bar.js模块中的声明与其他文件中的使用相匹配,则将bar.setBarOnFoo
重命名为bar.setFooBar
要容易得多。在所有文件中搜索和替换bar.setBarOnFoo到bar.setFooBar就可以完成任务。
exports
模块,应该在匿名函数的参数中将其命名为exports
。为什么?因为这是惯例。99.998%使用exports
机制的代码都遵循惯例。反对惯例会使您的代码更难阅读。实际上,exports
模块存在的唯一目的是支持惯例:即CommonJS模块习语。除非您要使用此习语(在示例代码中未使用),否则没有强制使用exports
的理由。 - Louisdefine("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();
这样,如果任何超出所需范围的代码(我知道不应该这样)调用了此要求的函数,则可以覆盖窗口对象。
我真的知道这不是一个“优雅”的解决方案,但在紧急情况下可能会对您有所帮助。
foo
对象了。 - Raynosfoo
为依赖项的模块才能使用它。 - Domenicbar
和baz
都能够访问“同一个对象实例”。 - Domenicfoo.theFoo =“不再是对象了,笨蛋!”
,这将破坏一切。 - Domenic