带构造函数的语义JavaScript单例

带构造函数的语义JavaScript单例,javascript,singleton,Javascript,Singleton,所以我想要一个名为client的类,它将是我用JavaScript编写的视频游戏的基础 client应该是一个只能有一个实例的类,但它的第一次创建应该由我自己在特定事件中设置,比如当用户单击“开始”按钮时 我为自己创建了一个singleton类,我启动它只是为了测试: // Singleton class for the client var client = (function() { // Public methods var _this = { construct: fu

所以我想要一个名为
client
的类,它将是我用JavaScript编写的视频游戏的基础

client
应该是一个只能有一个实例的类,但它的第一次创建应该由我自己在特定事件中设置,比如当用户单击“开始”按钮时

我为自己创建了一个singleton类,我启动它只是为了测试:

// Singleton class for the client
var client = (function() {

  // Public methods
  var _this = {
    construct: function() {
      delete _this.construct;
      _this.director = new lime.Director(document.body, window.innerWidth, window.innerHeight); // Setup the rendering engine
    }
  }
  return _this;
})();

// Fire when dependencies are loaded
window.onload = client.construct;
问题:


但我希望这是一个开源项目,最后一行是
client.construct
似乎是一个非常不寻常的惯例。我如何编写我的singleton类,以便它将使用
新客户端构建,并且永远不能再次构建?

这可能是您想要的:

var initialized = false;

function initialize() {
    if (!initialized) {
        window.client = new Client();
        initialized = true;
    }
}

window.onload = initialize;

你没有。在强类型语言中,通过使用私有构造函数并公开包含类的唯一实例的静态属性来生成单例。防止再次实例化的唯一方法是抛出异常,但这是更糟糕的设计。但是,您可能会延迟实例化对象:

// A wrapper function
var client = (function () {
    var client = function() {
       // Actual class
    }
    var instance = null;
    // Property to return the instance
    Object.defineProperty("instance", {
        get: function () {
            if (!instance) { instance = new client(); }
            return instance;
        }
    }
   return client;
})();

这是我最喜欢的singleton实例模式,它简单明了:

var mySingleton = new function() {
    // ...
}
因此,您可以:

var myClient = new function() {
    this.director = null;
    this.initialize = function() {
        // Setup the rendering engine
        this.director = new lime.Director(
            document.body, 
            window.innerWidth, 
            window.innerHeight
        );
    }
}

window.onload = myClient.initialize();
来源:

类似这样的内容:

var Client = (function ClientClass(){

  var instance;

  // Constructor
  function Client() {
    if (instance) throw 'Already instantiated';
    this.director = new lime.Director(...);
    instance = true;
  }

  Client.prototype = {
    // Public methods
  };

  return Client;

}());

您可以使用与常规面向对象语言相同的模式:将实例存储在“private”或“static”属性中(我引用这些术语是因为它们不完全适用于JavaScript)。在代码中,使用“private”属性来存储实例(static属性是构造函数的属性):


首先:你确定你真的想这么做吗?对于大多数简单的情况,最好不要使用
prototype
或使用
new
关键字,而只使用所需的属性和方法编写对象文本,或者在需要稍微复杂的构造逻辑时,使用一次性函数创建对象。简单是好的

我想有几种情况下,您可能希望在JavaScript中创建一个“传统”的单例,比如延迟实例化,或者如果您使用的是单例类的原型

在这种情况下,您可能希望尝试这种基于bfavaretto的方法,在这种方法中,类的用户希望通过调用
Client.getSingletonInstance()
而不是
new Client()
,来获取客户机对象,并且通过
new
实例化客户机发生在
getSingletonInstance()内部
方法

var Client = (function() {
    // Our "private" instance
    var instance;

    // The constructor
    function Client() {

        // If it's being called again, throw an error
        if (typeof instance != "undefined") {
            throw new Error("Client can only be instantiated once.");
        }

        // initialize here

        // Keep a closured reference to the instance
        instance = this;
    }

    // Add public methods to Client.prototype
    Client.prototype.myPublic = function() {

    }

    Client.getSingletonInstance = function() {
        if (typeof instance == "undefined") {
            return new this();
        }
        else {
            return instance;
        }
    }

    // Return the constructor
    return Client;
})();


var c1 = Client.getSingletonInstance();
var c2 = Client.getSingletonInstance();

console.log(c1 == c2); // true
我更喜欢这种方式,因为在我看来,让类的用户调用
new
,但实际上却没有得到一个新对象是一种误导


这可能会对你有所帮助:当我看到你的答案时,我差点把我的扔掉。它们非常相似,不同的是你在抛出一个错误,而我在返回实例时,如果已经有了一个实例。+1,但在我看来,你的两个答案中缺少的是某种
Client.getSingletonInstance()
方法,它创建并返回一个新实例,如果没有创建新实例,否则返回现有的。这难道不是单身模式的基本要点吗?让类的用户使用静态方法来获取实例,而不是调用new,但始终获取相同的实例?@MarkAmery:好吧,这就是bfavaretto的答案正确的地方,但是使用构造函数?这完全取决于您是否允许多次调用构造函数。@elclanrs了解我的意思。基本上,它将类的用户在bfavaretto的例子中使用“new”所做的事情转变为通过模拟“静态方法”来完成,如果类的用户试图用
new
第二次实例化它,就会抛出一个错误。我认为这两种方法都是最好的。
var Client = (function() {
    // Our "private" instance
    var instance;

    // The constructor
    function Client() {

        // If it's being called again, throw an error
        if (typeof instance != "undefined") {
            throw new Error("Client can only be instantiated once.");
        }

        // initialize here

        // Keep a closured reference to the instance
        instance = this;
    }

    // Add public methods to Client.prototype
    Client.prototype.myPublic = function() {

    }

    Client.getSingletonInstance = function() {
        if (typeof instance == "undefined") {
            return new this();
        }
        else {
            return instance;
        }
    }

    // Return the constructor
    return Client;
})();


var c1 = Client.getSingletonInstance();
var c2 = Client.getSingletonInstance();

console.log(c1 == c2); // true