7

newのようなキーワードでインスタンス化できるJavaScriptシングルトンを作成するこのかなり興味深い方法に出くわしましたvar x = new SingletonClass()。可変スコープやクロージャなどはかなりよく理解していますが、このコードブロックがなぜそのように機能するのかを正確に理解するのに苦労しています。

// EDIT: DO NOT USE this code; see the answers below
function SingletonClass() {
  this.instance = null;
  var things = [];

  function getInstance() {
    if (!this.instance) {
      this.instance = {
        add: function(thing) {
          things.push(thing);
        },
        list: function() {
          console.log(things.toString());
        }
      };
    }
    return this.instance;
  }

  return getInstance();
}

var obj1 = new SingletonClass();
obj1.add("apple");
obj1.list();  //"apple"

var obj2 = new SingletonClass();
obj2.add("banana");
obj1.list();  //"apple,banana"
obj2.list();  //"apple,banana"

obj1.add("carrot");
obj1.list();  //"apple,banana,carrot"
obj2.list();  //"apple,banana,carrot"

SingletonClass私の直感では、newがインスタンス化されるたびに、thisその新しい新しいオブジェクトを参照しますが、コンストラクターによって完全に別個のオブジェクトが返されるため、this破棄されるだけだと思います。しかし、それはぶらぶらしています。どのように?なんで?

ここで私が見逃している小さな詳細がいくつかあります。誰かがそれに光を当てることができますか?

編集:このコードは悪いことがわかりました。インスタンスへの参照を「魔法のように」保持しているように見える理由は、実際にはインスタンスをグローバルオブジェクトにサイレントに格納しているためです。それはせいぜい悪い習慣であり、間違いなくバグが発生しやすいです。

4

5 に答える 5

3

thisfunction の内部getInstance、つまりthisグローバル オブジェクトと混同しないでくださいwindow。したがって、オブジェクトを作成してウィンドウ オブジェクトに割り当てます。次にコンストラクタを呼び出すときに、window.instanceが存在するかどうかを確認します。

コードthis.instance = null;は意味がなく、混乱するだけです。削除しても何も変わりません。

以下はMDNからのものです。

コードnew foo(...)が実行されると、次のことが起こります。

  1. foo.prototype から継承して、新しいオブジェクトが作成されます。
  2. コンストラクター関数 foo が指定された引数で呼び出され、これが新しく作成されたオブジェクトにバインドされます。new foo は と同等です new foo()。つまり、引数リストが指定されていない場合、foo は引数なしで呼び出されます。
  3. コンストラクター関数によって返されるオブジェクトは、新しい式全体の結果になります。コンストラクター関数が明示的にオブジェクトを返さない場合は、代わりに手順 1 で作成されたオブジェクトが使用されます。(通常、コンストラクターは値を返しませんが、通常のオブジェクト作成プロセスをオーバーライドする場合は、値を返すことを選択できます。)

step3 に注意してください。コンストラクターに return ステートメントがある場合、返される結果は新しい式の結果になります。

于 2012-10-02T02:27:17.760 に答える
2

コンストラクターが呼び出すgetInstance()と、this内部のポインターgetInstance()が設定されるwindowため、そのthis.instance内部はグローバル変数になりますwindow.instance

このコードは、グローバル変数window.instanceを使用して 1 つのインスタンスを追跡するだけであり、実際よりもはるかに単純にすることができます。

これを実装するためのよりクリーンな方法はこれです。1 つのグローバル インスタンスは、最上位のグローバル変数ではなく、関数自体のプロパティとして格納されます。

    function SingletonClass() {
        var things = [];

        // if there is a previous instance, return it
        if (SingletonClass.inst) {
            return SingletonClass.inst;
        }
        // if not called with 'new', force it
        if (!this instanceof SingletonClass) {
            return new SingletonClass();
        }

        // remember the first created instance
        SingletonClass.inst = this;

        // add methods that can see our private `things` variable
        this.add = function(thing) {
           things.push(thing);
        }

        this.list = function() {
            console.log(things.toString());
        }
    }
于 2012-10-02T02:26:00.797 に答える
1

関数this内のキーワードはオブジェクトを示します。関数では、オブジェクトを示します。getInstanceWindowSingletonClassSingletonClass

字句スコープでは、関数addlist関数は常に同じthings配列を参照します。

于 2012-10-02T02:55:07.730 に答える
1

を使用していますwindow-はこれと同じではありませgetInstanceん:thisSingletonClass

function Singleton() {
    console.log("This is:", this);
    this.instance = null;

    function _get() {
        console.log("_get's this is:", this);
        if (!this.instance) {
           console.log("Instance is null");
           this.instance = { test: 1 };
        }
        return this.instance;
    }

    return _get();
}

var x = new Singleton();

// Output is:
This is: 
Singleton
_get's this is: 
Window
Instance is null

JavaScript でシングルトン パターンを実装するより良い方法は次のとおりです。

function Singleton() {

    // Handle not being called with `new`
    if (!this instanceof Singleton) {
        return new Singleton();
    }

    var instantiating = !!Singleton.instance,
        things = [];

    Singleton.instance = instantiating ? this : Singleton.instance;

    if (instantiating) {
        this.list = function() {
            console.log(things.toString());
        }

        this.add = function(item) {
            things.push(item);
        }
    }

    return Singleton.instance;
}

これにはまだ制限があります。たとえば、誰かがSingleton.instance別のオブジェクトに置き換えた場合、 の異なるインスタンス間のテストSingletonが壊れる可能性があります - 例:

var x = new Singleton();
// Har, har, we be breaking things!
Singleton.instance = {"not": "the", "same": "at", "all": true};
var y = new Singleton();

console.log(x === y); // false
于 2012-10-02T02:26:50.583 に答える
0

これは、シングルトンの良いリンクのようです:

JavaScriptでシングルトンを実装する最も簡単でクリーンな方法は?

上記の作業に関連する例を次に示します。

var myInstance = {
    method1: function () {
        return "Apple";
    },
    method2: function () {
        return "Orange";
    },
    banana: "Banana"
};

myInstance.grape = "Grape";

console.log(myInstance.method1(), myInstance.method2(), myInstance.banana, myInstance.grape);

ここに作業リンクがあります:

http://www.quirkscode.com/flat/forumPosts/singleton/singleton.html

于 2012-10-02T03:12:17.493 に答える