这个JS单例模式是如何工作的?

smi*_*lli 7 javascript singleton constructor this

我遇到了这种相当有趣的方式来创建一个可以用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"
Run Code Online (Sandbox Code Playgroud)

我的直觉说每次SingletonClass实例化时都会this引用新的新对象 - 但是由于构造函数返回了一个完全独立的对象,我认为它this会被丢弃.但它仍然存在.怎么样?为什么?

这里有一些微小的细节,我想念.任何人都能发光吗?

编辑:原来这个代码很糟糕.它"神奇地"似乎持有对实例的引用的原因是因为它实际上是静默地将它存储在全局对象中.这充其量是不好的做法,毫无疑问容易出错.

xda*_*azz 3

不要被this函数内部所迷惑getInstance,那this是全局对象window,所以你正在创建一个对象并分配给窗口对象,下次调用构造函数时,你正在检查是否window.instance存在。

该代码this.instance = null;毫无意义,只会让您感到困惑。删除它不会改变任何东西。

以下内容来自MDN

当代码new foo(...)执行时,会发生以下情况:

  1. 创建一个新对象,继承自 foo.prototype。
  2. 使用指定的参数调用构造函数 foo ,并将 this 绑定到新创建的对象。new foo 等价于 new foo(),即如果没有指定参数列表,则调用 foo 时不带参数。
  3. 构造函数返回的对象成为整个 new 表达式的结果。如果构造函数没有显式返回对象,则使用步骤 1 中创建的对象。(通常构造函数不返回值,但如果想要覆盖正常的对象创建过程,则可以选择这样做。)

注意步骤3,当构造函数中有return语句时,返回的结果将是new表达式的结果。