メインページレベルでコードを記述しており、2つの依存関係がオブジェクトの同じインスタンスを必要とし、それを依存関係として記述しているとします。これについて適切な方法は何ですか?
基本的に、私がやりたいことは、「この依存関係がロードされていない場合...ロードします。そうでない場合は、すでにロードされた同じインスタンスを使用して、そのインスタンスを渡します」です。
これをモジュールレベルの変数にします。例えば、
// 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を提供するだけです。
そして、その遅延読み込みを確認してください。最も簡単な方法は、クロスブラウザヘルパーを提供するアンダースコアのような抽象化ライブラリを使用することです。他のオプションは、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が提供するもの のようなポリフィルを含める必要があります。
別の(そしておそらく私の考えではより良い)アプローチは、メッセージングサービスオブジェクト自体をモジュールにすることです。これは次のようになります
// 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
};
});
モジュールを使用するすべての人に同じnotify
およびlisten
メソッドを公開し、それらは常に同じ privatelistenerMap
変数を参照するため、これはあなたがしたいことをする必要があります。また、Function.prototype.bind
、およびメッセージングサービス自体とそのシングルトン使用を強制するモジュールとの間のかなり不必要な区別を取り除きます。
これは、モジュール自体がそのモジュール内の変数ではなく共有変数であるバージョンです。
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' 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);
});
以下のコメントに対処するために、私は個人的に上記の規約が有用であることを発見しました。走行距離は異なる場合がありますが、便利だと思われる場合はお気軽に採用してください。コンベンションは、次の2つのルールに要約されます。
ファイル名を使用して、例えばほとんどの開発者はfoo.js依存関係のパラメーターとして「foo」を定義するため、foo.jsの名前は変数「foo」の場合、コードの可読性が向上します。コードをスキャンするか、grepを使用すると、モジュールの内外で使用する 'foo'へのすべての参照を簡単に見つけることができ、モジュールが公開しているものを簡単に選択できます。たとえば、bar.setBarOnFoo
からbar.setFooBar
は、bar.jsモジュールの宣言が他のファイルの使用法を反映している場合、はるかに簡単です。すべてのファイルでbar.setBarOnFooをbar.setFooBarに単純に検索して置換すると、タスクが完了します。