108

私は最近、Node.jsでシングルトンを書く方法についてこの記事に出くわしました。require 私は次のような州の文書を知っています:

モジュールは、最初にロードされた後にキャッシュされます。を複数回呼び出してrequire('foo')も、モジュールコードが複数回実行されることはありません。

したがって、必要なすべてのモジュールは、シングルトンボイラープレートコードなしでシングルトンとして簡単に使用できるようです。

質問:

上記の記事は、シングルトンを作成するための解決策についてのラウンドアバウトを提供していますか?

4

10 に答える 10

144

上記のすべては複雑すぎます。デザインパターンが実際の言語の欠陥を示していると言う考え方があります。

プロトタイプベースのOOP(クラスレス)を使用する言語は、シングルトンパターンをまったく必要としません。その場でシングル(トン)オブジェクトを作成し、それを使用するだけです。

ノード内のモジュールに関しては、はい、デフォルトでそれらはキャッシュされますが、たとえばモジュール変更のホットロードが必要な場合は微調整できます。

ただし、共有オブジェクトを全体的に使用する場合は、モジュールエクスポートに配置することで問題ありません。「シングルトンパターン」で複雑にしないでください。JavaScriptでは必要ありません。

于 2012-11-01T14:35:58.343 に答える
67

これは基本的にnodejsキャッシングと関係があります。簡潔でシンプル。

https://nodejs.org/api/modules.html#modules_caching

(v 6.3.1)

キャッシング

モジュールは、最初にロードされた後にキャッシュされます。これは、(とりわけ)require('foo')を呼び出すたびに、同じファイルに解決される場合、まったく同じオブジェクトが返されることを意味します。

require('foo')を複数回呼び出しても、モジュールコードが複数回実行されることはありません。これは重要な機能です。これを使用すると、「部分的に完了した」オブジェクトを返すことができるため、サイクルが発生する場合でも推移的な依存関係をロードできます。

モジュールにコードを複数回実行させる場合は、関数をエクスポートして、その関数を呼び出します。

モジュールキャッシングの警告

モジュールは、解決されたファイル名に基づいてキャッシュされます。モジュールは呼び出し元のモジュールの場所(node_modulesフォルダーからロード)に基づいて異なるファイル名に解決される可能性があるため、require('foo')が異なるファイルに解決される場合、常にまったく同じオブジェクトを返すという保証はありません。 。

さらに、大文字と小文字を区別しないファイルシステムまたはオペレーティングシステムでは、解決された異なるファイル名が同じファイルを指すことがありますが、キャッシュはそれらを異なるモジュールとして扱い、ファイルを複数回リロードします。たとえば、require('./ foo')とrequire('./ FOO')は、。/fooと./FOOが同じファイルであるかどうかに関係なく、2つの異なるオブジェクトを返します。

簡単に言えば。

シングルトンが必要な場合; オブジェクトをエクスポートします

シングルトンが必要ない場合。関数をエクスポートします(そして、その関数で何かを行う/何かを返す/何でもします)。

非常に明確にするために、これを適切に行うと機能するはずです。https://stackoverflow.com/a/33746703/1137669(Allen Luceの回答)を参照してください。異なる方法で解決されたファイル名が原因でキャッシュが失敗した場合に何が起こるかをコードで説明します。ただし、常に同じファイル名に解決する場合は、機能するはずです。

2016年の更新

es6シンボルを使用してnode.jsに真のシングルトンを作成する 別の解決策このリンク

2020年の更新

この回答は、CommonJS(Node.js独自のモジュールのインポート/エクスポート方法)に関するものです。Node.jsはおそらくECMAScriptモジュールに切り替えます:https ://nodejs.org/api/esm.html (知らなかった場合、ECMAScriptはJavaScriptの本名です)

ECMAScriptに移行するときは、今のところ以下をお読みください:https ://nodejs.org/api/esm.html#esm_writing_dual_packages_while_avoiding_or_minimizing_hazards

于 2016-08-10T13:31:36.837 に答える
30

いいえ 。ノードのモジュールキャッシングが失敗すると、そのシングルトンパターンは失敗します。OSXで意味のある形で実行されるように例を変更しました。

var sg = require("./singleton.js");
var sg2 = require("./singleton.js");
sg.add(1, "test");
sg2.add(2, "test2");

console.log(sg.getSocketList(), sg2.getSocketList());

これにより、作成者が予想した出力が得られます。

{ '1': 'test', '2': 'test2' } { '1': 'test', '2': 'test2' }

ただし、小さな変更を加えるとキャッシングが無効になります。OSXでは、次のようにします。

var sg = require("./singleton.js");
var sg2 = require("./SINGLETON.js");
sg.add(1, "test");
sg2.add(2, "test2");

console.log(sg.getSocketList(), sg2.getSocketList());

または、Linuxの場合:

% ln singleton.js singleton2.js

次に、sg2require行を次のように変更します。

var sg2 = require("./singleton2.js");

そして、バム、シングルトンは敗北しました:

{ '1': 'test' } { '2': 'test2' }

これを回避するための許容できる方法がわかりません。シングルトンのようなものを作成する必要性を本当に感じ、グローバル名前空間(および結果として生じる可能性のある多くの問題)を汚染しても問題がない場合は、作成者getInstance()exports行を次のように変更できます。

singleton.getInstance = function(){
  if(global.singleton_instance === undefined)
    global.singleton_instance = new singleton();
  return global.singleton_instance;
}

module.exports = singleton.getInstance();

とは言うものの、私はこのようなことをする必要がある本番システムの状況に遭遇したことはありません。また、Javascriptでシングルトンパターンを使用する必要性を感じたことはありません。

于 2015-11-16T23:45:43.327 に答える
22

モジュールドキュメントのモジュールキャッシングの警告をもう少し見てみましょう。

モジュールは、解決されたファイル名に基づいてキャッシュされます。モジュールは呼び出し元のモジュールの場所(node_modulesフォルダーからロード)に基づいて異なるファイル名に解決される可能性があるため、require('foo')が異なるファイルに解決される場合、常にまったく同じオブジェクトを返すという保証はありません。

したがって、モジュールが必要なときの場所に応じて、モジュールの別のインスタンスを取得することができます。

モジュールのように聞こえますが、シングルトンを作成するための単純なソリューションではありません。

編集:または多分彼らです。@mkoryakのように、1つのファイルが(シンボリックリンクを使用せずに)異なるファイル名に解決される場合を思い付くことができません。ただし(@JohnnyHKのコメントのとおり)、異なるnode_modulesディレクトリにあるファイルの複数のコピーは、それぞれ個別にロードおよび保存されます。

于 2012-11-01T16:43:36.437 に答える
19

そのようなnode.js(またはブラウザJS)のシングルトンは完全に不要です。

モジュールはキャッシュされてステートフルであるため、提供したリンクに示されている例は、はるかに簡単に簡単に書き直すことができます。

var socketList = {};

exports.add = function (userId, socket) {
    if (!socketList[userId]) {
        socketList[userId] = socket;
    }
};

exports.remove = function (userId) {
    delete socketList[userId];
};

exports.getSocketList = function () {
    return socketList;
};
// or
// exports.socketList = socketList
于 2012-11-01T14:32:36.123 に答える
12

jsでシングルトンを実行するために特別なことは何も必要ありません。記事のコードは次のようになります。

var socketList = {};

module.exports = {
      add: function() {

      },

      ...
};

node.jsの外部(たとえば、ブラウザーjs)では、ラッパー関数を手動で追加する必要があります(node.jsで自動的に実行されます)。

var singleton = function() {
    var socketList = {};
    return {
        add: function() {},
        ...
    };
}();
于 2012-11-01T14:31:08.827 に答える
12

ES6クラスを使用するここでの唯一の答え

// SummaryModule.js
class Summary {

  init(summary) {
    this.summary = summary
  }

  anotherMethod() {
    // do something
  }
}

module.exports = new Summary()

このシングルトンには次のものが必要です。

const summary = require('./SummaryModule')
summary.init(true)
summary.anotherMethod()

ここでの唯一の問題は、クラスコンストラクターにパラメーターを渡すことができないことですが、手動でinitメソッドを呼び出すことで回避できます。

于 2017-08-05T12:50:56.723 に答える
7

シングルトンはJSで問題ありませんが、それほど冗長である必要はありません。

ノードでは、たとえばサーバーレイヤーのさまざまなファイルで同じORM / DBインスタンスを使用するためにシングルトンが必要な場合、参照をグローバル変数に詰め込むことができます。

グローバル変数が存在しない場合はそれを作成し、それへの参照を返すモジュールを作成するだけです。

@ allen-luceは、脚注のコード例をここにコピーして、それを正しく理解しました。

singleton.getInstance = function(){
  if(global.singleton_instance === undefined)
    global.singleton_instance = new singleton();
  return global.singleton_instance;
};

module.exports = singleton.getInstance();

newただし、キーワードの使用は必須ではないことに注意してください。古いオブジェクト、関数、iifeなどはすべて機能します。ここではOOPブードゥーは発生しません。

参照を返す関数内のいくつかのobjを閉じて、その関数をグローバルにすると、ボーナスポイントが得られます。グローバル変数を再割り当てしても、すでに作成されているインスタンスが壊れるわけではありませんが、これは疑わしいほど便利です。

于 2016-06-06T15:33:36.143 に答える
1

シンプルに保つ。

foo.js

function foo() {

  bar: {
    doSomething: function(arg, callback) {
      return callback('Echo ' + arg);
    };
  }

  return bar;
};

module.exports = foo();

それならただ

var foo = require(__dirname + 'foo');
foo.doSomething('Hello', function(result){ console.log(result); });
于 2017-01-15T15:53:35.763 に答える
0

クラスを使いたいなら、それは最短で最も美しいです

module.exports = new class foo {...}
于 2021-11-10T09:25:56.420 に答える