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


当前回答

我的意见:我有一个构造函数(CF),例如,

var A = function(arg1){
  this.arg1 = arg1
};

我只需要这个CF创建的每个对象都是相同的。

var X = function(){
  var instance = {};
  return function(){ return instance; }
}();

Test

var x1 = new X();
var x2 = new X();
console.log(x1 === x2)

其他回答

我认为最简单的方法是声明一个简单的对象文字:

var myInstance = {
  method1: function () {
    // ...
  },
  method2: function () {
    // ...
  }
};

如果你想在你的单例实例上有私有成员,你可以这样做:

var myInstance = (function() {
  var privateVar = '';

  function privateMethod () {
    // ...
  }

  return { // public interface
    publicMethod1: function () {
      // All private members are accessible here
    },
    publicMethod2: function () {
    }
  };
})();

这被称为模块模式,它基本上允许您通过使用闭包来封装对象上的私有成员。

如果你想阻止对单例对象的修改,你可以使用ES5 object .freeze方法冻结它。

这将使对象不可变,防止对其结构和值进行任何修改。

如果你正在使用ES6,你可以很容易地使用ES模块来表示一个单例,你甚至可以通过在模块范围内声明变量来保持私有状态:

// my-singleton.js
const somePrivateState = []

function privateFn () {
  // ...
}

export default {
  method1() {
    // ...
  },
  method2() {
    // ...
  }
}

然后你可以简单地导入单例对象来使用它:

import myInstance from './my-singleton.js'
// ...

单例模式:

确保一个类只有一个实例,并提供对它的全局访问点。

单例模式将特定对象的实例数量限制为一个。这个单一实例称为单例。

定义返回唯一实例的getInstance()。 负责创建和管理实例对象。

单例对象被实现为一个即时匿名函数。函数立即执行,将它括在括号中,然后再加上两个括号。它被称为匿名,因为它没有名字。

示例程序

var Singleton = (function () { var instance; function createInstance() { var object = new Object("I am the instance"); return object; } return { getInstance: function () { if (!instance) { instance = createInstance(); } return instance; } }; })(); function run() { var instance1 = Singleton.getInstance(); var instance2 = Singleton.getInstance(); alert("Same instance? " + (instance1 === instance2)); } run()

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

/*************************************************
 *     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
*******************************************************************/

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

简单的例子

class Settings {

  constructor() {
    if (Settings.instance instanceof Settings) {
      return Settings.instance;
    }
    this.settings = {
      id: Math.floor(Math.random() * 4000),
      name: "background",
    };
    Object.freeze(this.settings);
    Object.freeze(this);
    Settings.instance = this;
  }

}

var o1 = new Settings();
var o2 = new Settings();

console.dir(o1);
console.dir(o2);

if (o1 === o2) {
  console.log("Matched");
}

对我来说,最干净的方法是:

const singleton = new class {
    name = "foo"
    constructor() {
        console.log(`Singleton ${this.name} constructed`)
    }
}

使用这种语法,您可以确定您的单例是并且将保持惟一的。您还可以享受类语法的甜蜜,并按预期使用它。

(注意,类字段需要节点v12+或现代浏览器。)