带构造函数的语义JavaScript单例

Semantic JavaScript singleton with constructor

本文关键字:JavaScript 单例 语义 构造函数      更新时间:2023-09-26

所以我想要一个名为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类,使其使用new Client构建,并且永远不能再构建?

首先:你确定你真的想这么做吗?对于大多数简单的情况,您可能根本不需要使用prototypenew关键字,而只需编写具有所需属性和方法的对象文字,或者如果需要稍微复杂的构造逻辑,则使用一次性函数创建对象。简洁是好的。

不过,我想有几种情况下,您可能想在JavaScript中创建一个"传统"的单例,比如延迟实例化,或者如果您使用的是涉及单例类原型的经典继承。

在这种情况下,您可能想尝试这种基于bfavaretto的方法,在这种方法中,类的用户希望通过调用Client.getSingletonInstance()而不是new Client()来获得Client对象,并且通过new实例化Client发生在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但实际上没有得到一个新对象是有误导性的。

http://jsfiddle.net/hBvSZ/3/

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

var Client = (function() {
    // Our "private" instance
    var instance;
    // The constructor
    function Client() {
        // If it's being called again, return the singleton instance
        if(typeof instance != "undefined") return instance;
        // initialize here
        // Keep a closured reference to the instance
        instance = this;
    }
    // Add public methods to Client.prototype
    Client.prototype.myPublic = function() {
    }
    // Return the constructor
    return Client;
})();

var c1 = new Client();
var c2 = new Client();
console.log(c1 == c2); // true

http://jsfiddle.net/hBvSZ/

类似这样的东西:

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;
}());

这可能是您想要的:

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

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

// 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;
})();

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

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();

来源:Alexander Podgorny 的Singleton模式