Простейший/Самый чистый способ реализовать singleton в JavaScript?

Каков самый простой/самый чистый способ реализации одноэлементного шаблона в JavaScript?

Ответ 1

Я думаю, что самый простой способ - объявить простой объектный литерал:

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

Если вы хотите, чтобы в вашем экземпляре синглтона были приватные участники, вы можете сделать что-то вроде этого:

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

  function privateMethod () {
    // ...
  }

  return { // public interface
    publicMethod1: function () {
      // all private members are accesible 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'
// ...

Ответ 2

Я думаю, что самый чистый подход - это что-то вроде:

var SingletonFactory = (function(){
    function SingletonClass() {
        //do stuff
    }
    var instance;
    return {
        getInstance: function(){
            if (instance == null) {
                instance = new SingletonClass();
                // Hide the constructor so the returned object can't be new'd...
                instance.constructor = null;
            }
            return instance;
        }
   };
})();

Впоследствии вы можете вызвать функцию как

var test = SingletonFactory.getInstance();

Ответ 3

Я не уверен, что согласен с тем, что шаблон модуля используется в качестве замены одноэлементного шаблона. Я часто видел, что синглтоны используются и подвергаются насилию в тех местах, где они совершенно не нужны, и я уверен, что шаблон модуля заполняет множество пробелов, когда программисты в противном случае используют одноэлемент, однако шаблон модуля не является одиночным.

шаблон модуля:

var foo = (function () {
    "use strict";
    function aPrivateFunction() {}
    return { aPublicFunction: function () {...}, ... };
}());

Все инициализированные в шаблоне модуля происходят, когда объявляется Foo. Кроме того, шаблон модуля может использоваться для инициализации конструктора, который затем может быть создан несколько раз. Хотя шаблон модуля является правильным инструментом для многих заданий, он не эквивалентен синглету.

singleton pattern:

короткая форма
var Foo = function () {
    "use strict";
    if (Foo._instance) {
        //this allows the constructor to be called multiple times
        //and refer to the same instance. Another option is to
        //throw an error.
        return Foo._instance;
    }
    Foo._instance = this;
    //Foo initialization code
};
Foo.getInstance = function () {
    "use strict";
    return Foo._instance || new Foo();
}
длинная форма с использованием шаблона модуля
var Foo = (function () {
    "use strict";
    var instance; //prevent modification of "instance" variable
    function Singleton() {
        if (instance) {
            return instance;
        }
        instance = this;
        //Singleton initialization code
    }
    //instance accessor
    Singleton.getInstance = function () {
        return instance || new Singleton();
    }
    return Singleton;
}());

В обеих версиях шаблона Singleton, который я предоставил, сам конструктор можно использовать как accessor:

var a,
    b;
a = new Foo(); //constructor initialization happens here
b = new Foo();
console.log(a === b); //true

Если вы не чувствуете себя комфортно, используя конструктор таким образом, вы можете выбросить ошибку в инструкции if (instance) и придерживаться длинной формы:

var a,
    b;
a = Foo.getInstance(); //constructor initialization happens here
b = Foo.getInstance();
console.log(a === b); //true

Следует также отметить, что одноэлементный шаблон хорошо сочетается с неявным шаблоном функции конструктора:

function Foo() {
    if (Foo._instance) {
        return Foo._instance;
    }
    //if the function wasn't called as a constructor,
    //call it as a constructor and return the result
    if (!(this instanceof Foo)) {
        return new Foo();
    }
    Foo._instance = this;
}
var f = new Foo(); //calls Foo as a constructor
-or-
var f = Foo(); //also calls Foo as a constructor

Ответ 4

В 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

Ответ 5

Существует несколько способов кошки кошки:) В зависимости от вашего вкуса или особых потребностей вы можете применить любое из предлагаемых решений. Я лично прихожу на первое решение CMS, когда это возможно (когда вам не нужна конфиденциальность). Поскольку вопрос был о простейшем и чистом, то победитель. Или даже:

var myInstance = {}; // done!

Это (цитата из моего блога)...

var SingletonClass = new function() { 
    this.myFunction() { 
        //do stuff 
    } 
    this.instance = 1; 
}

не имеет особого смысла (мой блог тоже не подходит), потому что ему не нужны какие-либо частные вары, поэтому он почти такой же, как:

var SingletonClass = { 
    myFunction: function () { 
        //do stuff 
    },
    instance: 1 
}

Ответ 6

Я осуждаю свой ответ, см. мой другой.

Обычно шаблон модуля (см. ответ CMS), который не является синтаксическим шаблоном, достаточно хорош. Однако одна из особенностей singleton заключается в том, что его инициализация задерживается до тех пор, пока объект не понадобится. В шаблоне модуля отсутствует эта функция.

Мое предложение (CoffeeScript):

window.singleton = (initializer) ->
  instance = undefined
  () ->
    return instance unless instance is undefined
    instance = initializer()

Что скомпилировано в JavaScript:

window.singleton = function(initializer) {
    var instance;
    instance = void 0;
    return function() {
        if (instance !== void 0) {
            return instance;
        }
        return instance = initializer();
    };
};

Тогда я могу сделать следующее:

window.iAmSingleton = singleton(function() {
    /* This function should create and initialize singleton. */
    alert("creating");
    return {property1: 'value1', property2: 'value2'};
});


alert(window.iAmSingleton().property2); // "creating" will pop up; then "value2" will pop up
alert(window.iAmSingleton().property2); // "value2" will pop up but "creating" will not
window.iAmSingleton().property2 = 'new value';
alert(window.iAmSingleton().property2); // "new value" will pop up

Ответ 7

Краткий ответ:

Поскольку неблокирующий характер JavaScript, синглтоны в JavaScript действительно уродливы в использовании. Глобальные переменные дадут вам один экземпляр через все приложение тоже без всех этих обратных вызовов, шаблон модуля аккуратно скрывает внутренности за интерфейсом. См. Ответ @CMS.

Но, поскольку вам нужен синглтон...

var singleton = function(initializer) {

  var state = 'initial';
  var instance;
  var queue = [];

  var instanceReady = function(createdInstance) {
    state = 'ready';
    instance = createdInstance;
    while (callback = queue.shift()) {
      callback(instance);
    }
  };

  return function(callback) {
    if (state === 'initial') {
      state = 'waiting';
      queue.push(callback);
      initializer(instanceReady);
    } else if (state === 'waiting') {
      queue.push(callback);
    } else {
      callback(instance);
    }
  };

};

Использование:

var singletonInitializer = function(instanceReady) {
  var preparedObject = {property: 'value'};
  // calling instanceReady notifies singleton that instance is ready to use
  instanceReady(preparedObject);
}
var s = singleton(singletonInitializer);

// get instance and use it
s(function(instance) {
  instance.doSomething();
});

Объяснение:

Синглтоны дают вам больше, чем один экземпляр через все приложение: их инициализация откладывается до первого использования. Это действительно большая вещь, когда вы имеете дело с объектами, инициализация которых стоит дорого. Дорогое, как правило, означает ввод-вывод, а в JavaScript ввода-вывода всегда означают обратные вызовы.

Не доверяйте ответам, которые дают вам интерфейс типа instance = singleton.getInstance(), все они пропускают точку.

Если они не принимают обратный вызов для запуска, когда экземпляр готов, тогда они не будут работать, когда инициализатор выполняет ввод/вывод.

Да, обратные вызовы всегда выглядят уродливее, чем вызов функции, который немедленно возвращает экземпляр объекта. Но опять же: когда вы делаете ввод-вывод, обратные вызовы являются обязательными. Если вы не хотите делать какие-либо операции ввода-вывода, тогда создание экземпляров достаточно дешево, чтобы сделать это при запуске программы.

Пример 1, дешевый инициализатор:

var simpleInitializer = function(instanceReady) {
  console.log("Initializer started");
  instanceReady({property: "initial value"});
}

var simple = singleton(simpleInitializer);

console.log("Tests started. Singleton instance should not be initalized yet.");

simple(function(inst) {
  console.log("Access 1");
  console.log("Current property value: " + inst.property);
  console.log("Let reassign this property");
  inst.property = "new value";
});
simple(function(inst) {
  console.log("Access 2");
  console.log("Current property value: " + inst.property);
});

Пример 2, инициализация с помощью ввода/вывода:

В этом примере setTimeout приводит к некоторой дорогостоящей операции ввода-вывода. Это иллюстрирует, почему синглтоны в JavaScript действительно нуждаются в обратных вызовах.

var heavyInitializer = function(instanceReady) {
  console.log("Initializer started");
  var onTimeout = function() {
    console.log("Initializer did his heavy work");
    instanceReady({property: "initial value"});
  };
  setTimeout(onTimeout, 500);
};

var heavy = singleton(heavyInitializer);

console.log("In this example we will be trying");
console.log("to access singleton twice before it finishes initialization.");

heavy(function(inst) {
  console.log("Access 1");
  console.log("Current property value: " + inst.property);
  console.log("Let reassign this property");
  inst.property = "new value";
});

heavy(function(inst) {
  console.log("Access 2. You can see callbacks order is preserved.");
  console.log("Current property value: " + inst.property);
});

console.log("We made it to the end of the file. Instance is not ready yet.");

Ответ 8

Я получил этот пример из Шаблоны JavaScript Создание лучших приложений с помощью шаблонов кодирования и проектирования Stoyan Stefanov, если вам нужен какой-то простой класс реализации, такой как singlelone, вы можете использовать следующую функцию:

var ClassName;

(function() {
    var instance;
    ClassName = function ClassName() {
        //If private instance variable already initialized return reference
        if(instance) {
            return instance;   
        }
        //If instance does not created save pointer of original reference
        //to 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 Singltone object using new prototype property
ClassName.prototype.nothing = true;
var obj_1 = new ClassName();
//Extending defined class like Singltone object using new prototype property
ClassName.prototype.everything = true; 
var obj_2 = new ClassName();

//Testing does this two object pointing to same instance
console.log(obj_1 === obj_2); //Result is true, it points to same instance object

//All prototype properites 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 is defined inside of constructor
console.log(obj_1.someProperty);// output 0
console.log(obj_2.someProperty);// output 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.

Ответ 9

Я думаю, что нашел самый чистый способ программирования на JavaScript, но вам нужно какое-то воображение. Я получил эту идею из рабочего метода в книге "javascript хорошие части".

Вместо использования нового ключевого слова вы можете создать такой класс:

function Class()
{
    var obj = {}; // Could also be used for inheritence if you don't start with an empty object.

    var privateVar;
    obj.publicVar;

    obj.publicMethod= publicMethod;
    function publicMethod(){} 

    function privateMethod(){} 

    return obj;
}

Вы можете создать экземпляр вышеуказанного объекта, сказав:

var objInst = Class(); // !!! NO NEW KEYWORD

Теперь, имея в виду этот метод работы, вы можете создать синглтон следующим образом:

ClassSingleton = function()
{
    var instance= null;

    function Class() // This is the class like the above one
    {
        var obj = {};
        return obj;
    }

    function getInstance()
    {
        if( !instance )
            instance = Class(); // Again no new keyword;

        return instance;
    }   

    return { getInstance : getInstance };

}();

Теперь вы можете получить свой экземпляр, позвонив

var obj = ClassSingleton.getInstance();

Я думаю, что это самый простой способ, поскольку полный "класс" даже не доступен.

Ответ 10

Следующие работы в узле v6

class Foo {
  constructor(msg) {

    if (Foo.singleton) {
      return Foo.singleton;
    }

    this.msg = msg;
    Foo.singleton = this;
    return Foo.singleton;
  }
}

Мы тестируем:

const f = new Foo('blah');
const d = new Foo('nope');
console.log(f); // => Foo { msg: 'blah' }
console.log(d); // => Foo { msg: 'blah' }

Ответ 11

@CMS и @zzzzBov получили замечательные ответы, но просто добавили мою собственную интерпретацию, основанную на том, что я перешел в тяжелую разработку node.js из PHP/Zend Framework, где шаблоны одноэлементности были обычными.

Следующий код, оформленный комментариями, основан на следующих требованиях:

  • может быть создан экземпляр только одного экземпляра объекта функции
  • экземпляр не является общедоступным и может быть доступен только через общедоступный метод.
  • конструктор не является общедоступным и может быть создан только в том случае, если еще нет доступного экземпляра
  • объявление конструктора должно позволить изменить цепочку прототипов. Это позволит конструктору наследовать от других прототипов и предложить "общедоступные" методы для экземпляра

Мой код очень похож на @zzzzBov, за исключением того, что я добавил цепочку прототипов к конструктору и дополнительные комментарии, которые должны помочь тем, кто приходит с PHP или аналогичным языком, перевести традиционный ООП на прототипный характер Javascripts. Это может быть не "самый простой", но я считаю, что это самое подходящее.

// declare 'Singleton' as the returned value of a self-executing anonymous function
var Singleton = (function () {
    "use strict";
    // 'instance' and 'constructor' should not be availble in a "public" scope
    // here they are "private", thus available only within 
    // the scope of the self-executing anonymous function
    var _instance=null;
    var _constructor = function (name) {
        this.name = name || 'default';
    }

    // prototypes will be "public" methods available from the instance
    _constructor.prototype.getName = function () {
        return this.name;
    }

    // using the module pattern, return a static object
    // which essentially is a list of "public static" methods
    return {
        // because getInstance is defined within the same scope
        // it can access the "private" 'instance' and 'constructor' vars
        getInstance:function (name) {
            if (!_instance) {
                console.log('creating'); // this should only happen once
                _instance = new _constructor(name);
            }
            console.log('returning');
            return _instance;
        }
    }

})(); // self execute

// ensure 'instance' and 'constructor' are unavailable 
// outside the scope in which they were defined
// thus making them "private" and not "public"
console.log(typeof _instance); // undefined
console.log(typeof _constructor); // undefined

// assign instance to two different variables
var a = Singleton.getInstance('first');
var b = Singleton.getInstance('second'); // passing a name here does nothing because the single instance was already instantiated

// ensure 'a' and 'b' are truly equal
console.log(a === b); // true

console.log(a.getName()); // "first"
console.log(b.getName()); // also returns "first" because it the same instance as 'a'

Обратите внимание, что технически самозаверяющая анонимная функция сама по себе является синглтоном, как хорошо продемонстрировано в коде, предоставленном @CMS. Единственный улов здесь заключается в том, что невозможно изменить цепочку прототипов конструктора, когда сам конструктор анонимен.

Имейте в виду, что для Javascript концепции "public" и "private" не применяются, как в PHP или Java. Но мы достигли такого же эффекта, используя правила доступности функциональной области Javascript.

Ответ 12

Не уверен, почему никто не поднял это, но вы могли просто сделать:

var singleton = new (function() {
  var bar = 123

  this.foo = function() {
    // whatever
  }
})()

Ответ 13

Самый ясный ответ должен быть приведен в этом документе из книги "Изучение шаблонов проектирования JavaScript" Адди Османи.

var mySingleton = (function () {
 
  // Instance stores a reference to the Singleton
  var instance;
 
  function init() {
 
    // Singleton
 
    // Private methods and variables
    function privateMethod(){
        console.log( "I am private" );
    }
 
    var privateVariable = "Im also private";
 
    var privateRandomNumber = Math.random();
 
    return {
 
      // Public methods and variables
      publicMethod: function () {
        console.log( "The public can see me!" );
      },
 
      publicProperty: "I am also public",
 
      getRandomNumber: function() {
        return privateRandomNumber;
      }
 
    };
 
  };
 
  return {
 
    // Get the Singleton instance if one exists
    // or create one if it doesn't
    getInstance: function () {
 
      if ( !instance ) {
        instance = init();
      }
 
      return instance;
    }
 
  };
 
})();

Ответ 14

Что случилось с этим?

function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}

Ответ 15

Могу ли я поставить свои 5 монет. У меня есть функция-конструктор, например.

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

Мне нужно сделать только то, что каждый объект, созданный этим CF, будет таким же.

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

Тест

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

Ответ 16

Я считаю, что это самый простой/самый чистый и интуитивно понятный способ, хотя для этого требуется ES7:

export default class Singleton {

  static instance;

  constructor(){
    if(instance){
      return instance;
    }

    this.state = "duke";
    this.instance = this;
  }

}

Исходный код: adam-bien.com

Ответ 17

Вот простой пример, объясняющий шаблон синглтона в javascript.

 var Singleton=(function(){
      var instance;
      var init=function(){
           return {
             display:function(){
             alert("This is a Singleton patern demo");
              }
            };
           }; 
            return {
              getInstance:function(){
                   if(!instance){
                     alert("Singleton check");
                      instance=init();
                       }
               return instance;
             }
         };

    })();

   // In this call first display alert("Singleton check")
  // and then alert("This is a Singleton patern demo");
  // It means one object is created

    var inst=Singleton.getInstance();
    inst.display();

    // In this call only display alert("This is a Singleton patern demo")
   //  it means second time new object is not created, 
   //  it uses the already created object 

    var inst1=Singleton.getInstance();
    inst1.display();

Ответ 18

Я нашел следующее, что является самым простым шаблоном Singleton, потому что использование нового оператора делает это немедленно доступным внутри функции, исключая необходимость возврата литерала объекта:

var singleton = new (function () {

  var private = "A private value";
  
  this.printSomething = function() {
      console.log(private);
  }
})();

singleton.printSomething();

Ответ 19

Мне понадобилось несколько синглетонов с:

  • ленивая инициализация
  • начальные параметры

и именно так я и придумал:

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 должен быть массивом для работы, поэтому, если у вас есть пустые переменные, просто перейти в []

  • Я использовал объект окна в функции, но я мог бы пройти в параметре, чтобы создать свою собственную область

  • имя и конструктивные параметры - это только строка для работы окна [], но с некоторыми простыми проверками типов, window.name и window.construct также возможны.

Ответ 20

Схема модуля: в "более читаемом стиле". Вы легко видите, какие методы являются общественностью, а какие - частными.

var module = (function(_name){
   /*Local Methods & Values*/
   var _local = {
      name : _name,
      flags : {
        init : false
      }
   }

   function init(){
     _local.flags.init = true;
   }

   function imaprivatemethod(){
     alert("hi im a private method");
   }

   /*Public Methods & variables*/

   var $r = {}; //this object will hold all public methods.

   $r.methdo1 = function(){
       console.log("method1 call it");
   }

   $r.method2 = function(){
      imaprivatemethod(); //calling private method
   }

   $r.init = function(){
      inti(); //making init public in case you want to init manually and not automatically
   }

   init(); //automatically calling init method

   return $r; //returning all publics methods

})("module");

теперь вы можете использовать методы публики, например

module.method2();//- > Я вызываю закрытый метод над предупреждением об общественном методе ( "привет личный метод" )

http://jsfiddle.net/ncubica/xMwS9/

Ответ 21

Как насчет этого способа, просто убедитесь, что класс не может быть новым.

Таким образом, вы можете использовать instanceof op, также вы можете использовать цепочку прототипов, чтобы наследовать класс, это обычный класс, но не может его новый, если yuu хочет получить экземпляр, просто используйте getInstance

function CA()
{
    if(CA.instance)
    {
        throw new Error('can not new this class');
    }else{
        CA.instance = this;
    }

}
/**
 * @protected
 * @static
 * @type {CA}
 */
CA.instance = null;
/** @static */
CA.getInstance = function()
{
    return CA.instance;
}

CA.prototype = 
/** @lends CA#*/
{
    func: function(){console.log('the func');}
}
// initilize the instance
new CA();

// test here
var c = CA.getInstance()
c.func();
console.assert(c instanceof CA)
// this will failed
var b = new CA();

Если вы не хотите раскрывать член instance, просто поместите его в закрытие.

Ответ 22

function Unicode()
  {
  var i = 0, unicode = {}, zero_padding = "0000", max = 9999;
  //Loop through code points
  while (i < max) {
    //Convert decimal to hex value, find the character, then pad zeroes to the codepoint
    unicode[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4);
    i = i + 1;
    }

  //Replace this function with the resulting lookup table
  Unicode = unicode;
  }

//Usage
Unicode();
//Lookup
Unicode["%"]; //returns 0025

Ответ 23

Ниже приведен фрагмент моего прохода, чтобы реализовать одноэлементный шаблон. Это происходило со мной во время собеседования, и я чувствовал, что должен это где-то захватить.

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

  //since there are no 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 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 gave 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
  *******************************************************************/

то же самое можно найти на моей странице gist

Ответ 24

Разве это тоже нет?

function Singleton() {
    var i = 0;
    var self = this;

    this.doStuff = function () {
        i = i + 1;
        console.log( 'do stuff',i );
    };

    Singleton = function () { return self };
    return this;
}

s = Singleton();
s.doStuff();

Ответ 25

Вы можете сделать это с помощью декораторов, как в этом примере ниже, для TypeScript:

class YourClass {

    @Singleton static singleton() {}

}

function Singleton(target, name, descriptor) {
    var instance;
    descriptor.value = () => {
        if(!instance) instance = new target;
        return instance;
    };
}

Затем вы используете свой синглтон следующим образом:

var myInstance = YourClass.singleton();

На момент написания этой статьи декораторы не были легко доступны в машинах JavaScript. Вам нужно будет убедиться, что в вашей среде исполнения JavaScript есть декораторы, которые действительно включены или используют компиляторы, такие как Babel и TypeScript.

Также обратите внимание, что экземпляр singleton создается "ленивым", т.е. создается только при его использовании в первый раз.

Ответ 26

Singleton:

Убедитесь, что класс имеет только один экземпляр и предоставляет глобальную точку доступа к нему.

Шаблон Singleton ограничивает количество экземпляров определенного объекта только одним. Этот единственный экземпляр называется singleton.

  • определяет getInstance(), который возвращает уникальный экземпляр.
  • ответственен за создание и управление объектом экземпляра.

Объект Singleton реализуется как немедленная анонимная функция. Функция выполняется немедленно, обернув ее в скобки, а затем две дополнительные скобки. Он называется анонимным, потому что он не имеет имени.

Пример программы

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

Ответ 27

Мне нравится использовать комбинацию Синглтона с шаблоном модуля, ветвь времени init-time с глобальной проверкой NS, завернутую в закрытие. В случае, когда окружающая среда не будет меняться после инициализации синглтона; использование немедленно вызываемого объекта-литерала для возврата модуля, полного утилит, которые будут сохраняться на некоторое время, должно быть прекрасным. Я не передаю никаких зависимостей, просто ссылаюсь на одиночные игры в их собственном маленьком мире - единственная цель: создать модуль служебных программ для привязки/развязки событий (в этом случае также могут работать и изменения ориентации устройства/ориентации).

window.onload = ( function( _w ) {
            console.log.apply( console, ['it', 'is', 'on'] );
            ( {
                globalNS : function() {
                    var nameSpaces = ["utils", "eventUtils"],
                        nsLength = nameSpaces.length,
                        possibleNS = null;

                    outerLoop:
                    for ( var i = 0; i < nsLength; i++ ) {
                        if ( !window[nameSpaces[i]] ) {
                            window[nameSpaces[i]] = this.utils;
                            break outerLoop;
                        };
                    };
                },
                utils : {
                    addListener : null,
                    removeListener : null
                },
                listenerTypes : {
                    addEvent : function( el, type, fn ) {
                        el.addEventListener( type, fn, false );
                    },
                    removeEvent : function( el, type, fn ) {
                        el.removeEventListener( type, fn, false );
                    },
                    attachEvent : function( el, type, fn ) {
                        el.attachEvent( 'on'+type, fn );
                    },
                    detatchEvent : function( el, type, fn ) {
                        el.detachEvent( 'on'+type, fn );
                    }
                },
                buildUtils : function() {
                    if ( typeof window.addEventListener === 'function' ) {
                        this.utils.addListener = this.listenerTypes.addEvent;
                        this.utils.removeListener = this.listenerTypes.removeEvent;
                    } else {
                        this.utils.attachEvent = this.listenerTypes.attachEvent;
                        this.utils.removeListener = this.listenerTypes.detatchEvent;
                    };
                    this.globalNS();
                },
                init : function() {
                    this.buildUtils();
                }
            } ).init();
        }( window ) );

Ответ 28

Вы не сказали "в браузере". В противном случае вы можете использовать модули NodeJS. Эти одинаковы для каждого вызова require. Основной пример:

Содержимое foo.js:

const circle = require('./circle.js');
console.log( `The area of a circle of radius 4 is ${circle.area(4)}`);

Содержимое circle.js:

const PI = Math.PI;

exports.area = (r) => PI * r * r;

exports.circumference = (r) => 2 * PI * r;

Обратите внимание, что вы не можете получить доступ к circle.PI, так как он не экспортируется.

Хотя это не работает в браузере, оно просто и чисто.

Ответ 29

Основной ключ - это значение Undertand Closure за этим. Таким образом, свойство внутри внутренней функции будет закрыто с помощью закрытия.

var Singleton = function() {    var instance;

 function init() {

    function privateMethod() {
        console.log("private via closure");
    }

    var privateVariable = "Private Property";

    var privateRandomNumber = Math.random();// this also private

    return {
        getRandomNumber: function () {  // access via getter in init call
            return privateRandomNumber;
        }

    };

};

return {
    getInstance: function () {

        if (!instance) {
            instance = init();
        }

        return instance;
    }

};

};

Ответ 30

Вы можете вернуть один и тот же экземпляр в каждом new исполнении -

function Singleton() {
    // lazy 
    if (Singleton.prototype.myInstance == undefined) {
        Singleton.prototype.myInstance = { description: "I am the instance"};
    }
    return Singleton.prototype.myInstance;
}

a = new Singleton();
b = new Singleton();
console.log(a); // { description: "I am the instance"};
console.log(b); // { description: "I am the instance"};
console.log(a==b); // true