用JavaScript实现单例模式的最简单/最干净的方法是什么?


当前回答

let MySingleton = (function () {
  var _instance
  function init() {
    if(!_instance) {
      _instance = { $knew: 1 }
    }
    return _instance
  }
  let publicAPIs = {
    getInstance: function() {
      return init()
    }
  }
  // this prevents customize the MySingleton, like MySingleton.x = 1
  Object.freeze(publicAPIs) 
  // this prevents customize the MySingleton.getInstance(), like MySingleton.getInstance().x = 1
  Object.freeze(publicAPIs.getInstance())
  return publicAPIs
})();

其他回答

我从JavaScript模式中得到了这个例子 用编码和设计模式构建更好的应用程序一书(Stoyan Stefanov著)。如果你需要一些简单的实现类,比如单例对象,你可以使用一个直接函数,如下所示:

var ClassName;

(function() {
    var instance;
    ClassName = function ClassName() {
        // If the private instance variable is already initialized, return a reference
        if(instance) {
            return instance;
        }
        // If the instance is not created, save a pointer of the original reference
        // to the private instance variable.
        instance = this;

        // All constructor initialization will be here
        // i.e.:
        this.someProperty = 0;
        this.someMethod = function() {
            // Some action here
        };
    };
}());

你可以通过下面的测试用例来检查这个例子:

// Extending defined class like singleton object using the new prototype property
ClassName.prototype.nothing = true;
var obj_1 = new ClassName();

// Extending the defined class like a singleton object using the new prototype property
ClassName.prototype.everything = true;
var obj_2 = new ClassName();

// Testing makes these two objects point to the same instance
console.log(obj_1 === obj_2); // Result is true, and it points to the same instance object

// All prototype properties work
// no matter when they were defined
console.log(obj_1.nothing && obj_1.everything
            && obj_2.nothing && obj_2.everything); // Result true

// Values of properties which are defined inside of the constructor
console.log(obj_1.someProperty); // Outputs 0
console.log(obj_2.someProperty); // Outputs 0

// Changing property value
obj_1.someProperty = 1;

console.log(obj_1.someProperty); // Output 1
console.log(obj_2.someProperty); // Output 1

console.log(obj_1.constructor === ClassName); // Output true

这种方法通过了所有测试用例,而当使用原型扩展时,私有静态实现将失败(它可以被修复,但并不简单),而公共静态实现则不太可取,因为实例是公开给公众的。

jsFiddly demo.

下面是我实现单例模式的演练片段。这是我在面试过程中想到的,我觉得我应该在某个地方捕捉到这一点。

/*************************************************
 *     SINGLETON PATTERN IMPLEMENTATION          *
 *************************************************/

// Since there aren't any classes in JavaScript, every object
// is technically a singleton if you don't inherit from it
// or copy from it.
var single = {};


// Singleton Implementations
//
// Declaring as a global object...you are being judged!

var Logger = function() {
  // global_log is/will be defined in the GLOBAL scope here
  if(typeof global_log === 'undefined'){
    global_log = this;
  }
  return global_log;
};


// The below 'fix' solves the GLOABL variable problem, but
// the log_instance is publicly available and thus can be
// tampered with.
function Logger() {
  if(typeof Logger.log_instance === 'undefined') {
    Logger.log_instance = this;
  }

  return Logger.log_instance;
};


// The correct way to do it to give it a closure!

function logFactory() {
  var log_instance; // Private instance
  var _initLog = function() { // Private init method
    log_instance = 'initialized';
    console.log("logger initialized!")
  }
  return {
    getLog : function(){ // The 'privileged' method
      if(typeof log_instance === 'undefined') {
        _initLog();
      }
      return log_instance;
    }
  };
}


/***** TEST CODE ************************************************

// Using the Logger singleton
var logger = logFactory(); // Did I just give LogFactory a closure?

// Create an instance of the logger
var a = logger.getLog();

// Do some work
// Get another instance of the logger
var b = logger.getLog();

// Check if the two logger instances are same
console.log(a === b); // true
*******************************************************************/

同样可以在我的主旨页面上找到。

我需要几个单人间:

延迟初始化 初始参数

这就是我想到的:

createSingleton ('a', 'add', [1, 2]);
console.log(a);

function createSingleton (name, construct, args) {
    window[name] = {};
    window[construct].apply(window[name], args);
    window[construct] = null;
}

function add (a, b) {
    this.a = a;
    this.b = b;
    this.sum = a + b;
}

args必须是Array,所以如果你有空变量,只需要传入[] 我在函数中使用了window对象,但我本可以传入一个参数来创建自己的作用域 name和构造参数只是字符串,以便window[]工作,但有一些简单的类型检查,window.name和window.name。构造也是可能的。

let MySingleton = (function () {
  var _instance
  function init() {
    if(!_instance) {
      _instance = { $knew: 1 }
    }
    return _instance
  }
  let publicAPIs = {
    getInstance: function() {
      return init()
    }
  }
  // this prevents customize the MySingleton, like MySingleton.x = 1
  Object.freeze(publicAPIs) 
  // this prevents customize the MySingleton.getInstance(), like MySingleton.getInstance().x = 1
  Object.freeze(publicAPIs.getInstance())
  return publicAPIs
})();

2015年ECMAScript (ES6):

class Singleton {
  constructor () {
    if (!Singleton.instance) {
      Singleton.instance = this
    }
    // Initialize object
    return Singleton.instance
  }
  // Properties & Methods
}

const instance = new Singleton()
Object.freeze(instance)

export default instance