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

300

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

Якуб Арнольд
источник
15
Даунвот за принятый ответ совсем не одиночка. Это просто глобальная переменная.
mlibby
5
Это тонна информации, но на самом деле выявляются различия между различными шаблонами проектирования JS. Это мне очень помогло
Джастин

Ответы:

315

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

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

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

ОБНОВЛЕНИЕ: Я хотел бы добавить, что если вы хотите предотвратить модификацию одноэлементного объекта, вы можете заморозить его , используя Object.freezeметод ES5 .

Это сделает объект неизменным, предотвращая любые изменения его структуры и значений.

Кроме того, я хотел бы отметить, что если вы используете ES6, вы можете очень легко представлять одиночный файл с помощью модулей ES , и вы даже можете хранить приватное состояние , объявив переменные в области видимости модуля :

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

function privateFn () {
  // ...
}

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

Затем вы можете просто импортировать объект singleton, чтобы использовать его:

import myInstance from './my-singleton.js'
// ...
CMS
источник
46
+1 Не странно ли искать «шаблон Singleton» в языке с глобальными переменными ???
Виктор
4
Используя шаблон модуля, как публичный член получит доступ к другому публичному члену? Т.е. как бы publicMethod1позвонить publicMethod2?
typeof
4
@ Да, шаблон родился на языках ООП, основанных на классах - я помню много реализаций, которые включали статический getInstanceметод и закрытый конструктор, но IMO, это самый «простой» способ создания одноэлементного объекта. в Javascript, и в конце он встречает ту же цель - единственный объект, который вы не можете инициализировать снова (нет конструктора, это просто объект) -. О коде вы связаны, у него есть какие - то проблемы, поменять местами aи bобъявления переменных и тест a === window. Приветствия.
CMS
15
@Victor - Это не странно искать «шаблон синглтона» на таком языке. во многих объектно-ориентированных языках используются глобальные переменные, и все еще используются синглтоны. Синглтон не только гарантирует, что будет только один объект данного класса. У Singleton есть еще несколько функций: 1) он должен быть инициализирован при первом использовании (что означает не только отложенную инициализацию, но также гарантирует, что объект действительно готов к использованию) 2) он должен быть потокобезопасным. Шаблон модуля может быть заменой шаблону синглтона, но только в браузере (и не всегда).
Скали
55
Это не должно быть принятым ответом. Это не синглтон вообще! Это просто глобальная переменная. Существует огромная разница между ними.
mlibby
172

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

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();
sebarmeli
источник
4
Замечание: оригинальный конструктор можно снова прочитать, используя delete instance.constructor:x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Роб W
var test = SingletonClass.getInstance () - выглядит не очень чисто или как JS. Другие души, которые заканчиваются на a = new Foo (); b = новый Foo (); a === b // true
Матиас
3
Пахнет больше как фабрика для меня со всей частью "getInstance".
Лайош Месарос
1
Это не одиночка, потому что вы можете создать несколько его экземпляров с помощью Object.create.
AndroidDev
Скрипка сломана
HoffZ
103

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

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

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

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

синглтон

короткая форма
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, который я предоставил, сам конструктор может использоваться как средство доступа:

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
zzzzBov
источник
4
Я никогда не говорил ничего о том, что синглеты - плохая или хорошая идея. Я сказал, что ваша реализация singleton намного сложнее, чем нужно, потому что вы путаете ограничения Java с шаблоном, как будто вы его совсем не понимаете. Это похоже на реализацию шаблона стратегии путем создания функций и методов конструктора, когда вы можете просто использовать анонимные функции для этого в Javascript.
Esailija
11
@Esailija, мне кажется, ты не понимаешь шаблон синглтона. Одноэлементный шаблон - это шаблон проекта, который ограничивает создание класса одним объектом. var singleton = {}не подходит под это определение.
zzzzBov
9
Это относится только к таким языкам, как Java, из-за их ограничений. var singleton = {}Вот как вы реализуете синглтон в Javascript .
Esailija
2
@Esailija, «В языке программирования на основе прототипов, где используются объекты, но не классы ...» В JavaScript есть концепция классов, поэтому она не применяется.
zzzzBov
3
Кстати. Singleton - это анти-шаблон, и его никогда не следует использовать в управляемой изолированной среде, такой как JavaScript. misko.hevery.com/2008/08/17/singletons-are-pathological-liars - youtube.com/watch?v=G32acYVd9LY blogs.msdn.com/b/scottdensmore/archive/2004/05/25/140827.aspx - jalf.dk/blog/2010/03/… - kore-nordmann.de/blog/0103_static_considered_harmful.html - phparch.com/2010/03/static-methods-vs-singletons-choose-neither
Бенджамин Грюнбаум
18

В 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
Xaqron
источник
1
Замораживание будет иметь смысл, если Синглтон будет оберткой вокруг какого-то другого класса и будет иметь только instanceполе. Поскольку он в данный момент ( instanceустановлен this), этот класс может иметь и другие поля, и заморозка не имеет смысла.
thisismydesign
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' }
Даниил
источник
8

В ES6 правильный способ сделать это:

class MyClass {
  constructor() {
    if (MyClass._instance) {
      throw new Error("Singleton classes can't be instantiated more than once.")
    }
    MyClass._instance = this;

    // ... your rest of the constructor code goes after this
  }
}

var instanceOne = new MyClass() // Executes succesfully 
var instanceTwo = new MyClass() // Throws error

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

class MyClass {
  constructor() {
    if (MyClass._instance) {
      return MyClass._instance
    }
    MyClass._instance = this;

    // ... your rest of the constructor code goes after this
  }
}

var instanceOne = new MyClass()
var instanceTwo = new MyClass()

console.log(instanceOne === instanceTwo) // logs "true"

UtkarshPramodGupta
источник
Здравствуйте, вы можете помочь мне узнать разницу между _instance и экземпляром, так как я использовал экземпляр и код не работал
Abhinav bhardwaj
Там нет технической разницы в instanceи _instance. Это просто соглашение об именах в языках программирования, в котором мы называем частные переменные с добавлением подчеркивания. Я подозреваю, что причина вашего кода не работает в том, что вы используете this.instanceвместоMyClass.instance
UtkarshPramodGupta
7

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

var myInstance = {}; // done!

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

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

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

var SingletonClass = { 
    myFunction: function () { 
        //do stuff 
    },
    instance: 1 
}
Стоян
источник
Фрагмент кода 2 содержит синтаксическую ошибку. Вы не можете написатьthis.f(){}
xoxox
7

Я не одобряю мой ответ, см. Мой другой .

Обычно шаблон модуля (см. Ответ 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
skalee
источник
Зачем вам загружать модуль, если он не нужен? И когда вам нужно загрузить модуль, то вы загружаете модуль, и он инициализируется.
Esailija
6

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

Поскольку неблокирующая природа JavaScript, Singletons в 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's reassign this property");
  inst.property = "new value";
});
simple(function(inst) {
  console.log("Access 2");
  console.log("Current property value: " + inst.property);
});

Пример 2, инициализация с I / O:

В этом примере 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's 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.");
skalee
источник
Из-за испытаний и невзгод с другими одноэлементными ответами, которые его не обрезали, я нашел код, поразительно похожий на этот.
Мейман
По той или иной причине, это единственный ответ, который имеет смысл для меня. Все остальные ответы напоминают мне об эпизоде ​​шоу, где трое мужчин пытаются взобраться на стену высотой четыре человека, рекурсивно взбираясь на плечи друг друга.
Тим Огилви
Укладка обратного вызова - это то, что мне действительно нужно! Спасибо!!
Тим Огилви
Такой подход никогда не дает вам синглтон: singleton (singletonInitializer)! == singleton (singletonInitializer) это два разных экземпляра. Результирующая функция, которую вы вернули, может использоваться для присоединения большего количества обратных вызовов к экземпляру, но строго не указывает, что может быть создан только один экземпляр этого типа. В этом весь смысл синглтона.
Оуэн
6

Я получил этот пример из JavaScript Patterns. Создайте лучшие приложения с помощью шаблонов кодирования и дизайна. По книге Стояна Стефанова, если вам нужен какой-то простой класс реализации, такой как объект singltone, вы можете использовать непосредственную функцию следующим образом:

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 демо.

Хамидулла
источник
5

Я думаю, что нашел самый чистый способ программирования на 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();

Я думаю, что это самый удачный способ, так как полный «Класс» даже недоступен.

Дэвид
источник
Но с этой техникой у вас может быть более одного экземпляра. Это неправильно.
nicolascolman
1
Я бы так не подумал, вы даже не можете получить доступ к классу без прохождения getInstance. Не могли бы вы уточнить?
Дэвид
David Maes Извините, но я не заметил проверки во втором примере. Я извиняюсь.
nicolascolman
4

@CMS и @zzzzBov оба дали замечательные ответы, но просто добавили мою собственную интерпретацию, основанную на том, что я перешел в разработку heavy 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's the same instance as 'a'

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

Помните, что в Javascript понятия «открытый» и «закрытый» не применяются, как в PHP или Java. Но мы достигли того же эффекта, используя правила доступности функциональных возможностей Javascript.

talentedmrjones
источник
Из вашего кода можно создать несколько экземпляров:var a = Singleton.getInstance('foo'); var b = new a.constructor('bar');
zzzzBov
@zzzzBov: Я просто получаю ошибки, пытаясь это сделать на своей скрипке: jsfiddle.net/rxMu8
cincodenada
4

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

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

  this.foo = function() {
    // whatever
  }
})()
Дерек Чианг
источник
Похоже, это отличный способ пропустить метод getInstance и получить более простое решение. Но имейте в виду, что синглтон будет выполняться, как только файл будет проанализирован, а это означает, что слушатели DOM должны быть обернуты в функцию $ (document) .ready
HoffZ
4

Самый ясный ответ должен быть этот из книги «Изучение шаблонов дизайна 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;
    }
 
  };
 
})();

令狐 葱
источник
3

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

export default class Singleton {

  static instance;

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

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

}

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

Альт Эйзен
источник
Это совершенно неправильно и может вызвать ошибку при вызовеnew Singleton()
UtkarshPramodGupta
2

Что с этим не так?

function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}
Manav
источник
Test = Klass; t1 = new Test(); t2 = new Test();- нет возможности переименовать класс или выбрать другое пространство имен.
zzzzBov
2

Могу ли я положить свои 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)
Olencha
источник
2

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

var singleton = new (function () {

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

singleton.printSomething();

отметка
источник
2

Вот простой пример для объяснения синглтон-паттерна в 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();
Шео Дайал Сингх
источник
1

Мне нужно было несколько синглетонов с:

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

и вот что я придумал:

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

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

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

Фред
источник
1

Как насчет этого, просто застраховать, класс не может снова новый.

Таким образом, вы можете использовать instanceofопцию, также вы можете использовать цепочку прототипов для наследования класса, это обычный класс, но вы не можете его создать, если вы хотите получить экземпляр, просто используйте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участника, просто поместите его в закрытие.

Wener
источник
1

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

/*************************************************
   *     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 странице

curioussam
источник
1
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
оборота Пол Свитт
источник
1

Разве это не синглтон тоже?

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();
Наваль
источник
1

Вы можете сделать это с помощью декораторов, как в следующем примере для 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 создается «ленивым», т. Е. Создается только при первом его использовании.

Vad
источник
1

Шаблон модуля: в «более читаемом стиле». Вы можете легко увидеть, какие методы являются публичными, а какие приватными

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/

ncubica
источник
1

Синглтон:

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

Шаблон 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()

Мохидин бен Мухаммед
источник
1

Simplest / Cleanest для меня означает также простое понимание и никаких наворотов, о чем много говорится в Java-версии обсуждения:

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

Ответ, который подошел бы самым простым / чистым лучше всего с моей точки зрения:

https://stackoverflow.com/a/70824/1497139

И это может быть только частично переведено на JavaScript. Некоторые из различий в Javascript:

  • конструкторы не могут быть частными
  • Классы не могут иметь объявленные поля

Но учитывая последний синтаксис ECMA, можно приблизиться к:

Шаблон Singleton как пример класса JavaScript

 class Singleton {

  constructor(field1,field2) {
    this.field1=field1;
    this.field2=field2;
    Singleton.instance=this;
  }

  static getInstance() {
    if (!Singleton.instance) {
      Singleton.instance=new Singleton('DefaultField1','DefaultField2');
    }
    return Singleton.instance;
  }
}

Пример использования

console.log(Singleton.getInstance().field1);
console.log(Singleton.getInstance().field2);

Пример результата

DefaultField1
DefaultField2
Вольфганг Фаль
источник
1
function Once() {
    return this.constructor.instance || (this.constructor.instance = this);
}

function Application(name) {
    let app = Once.call(this);

    app.name = name;

    return app;
}

Если вы в классах:

class Once {
    constructor() {
        return this.constructor.instance || (this.constructor.instance = this);
    }
}

class Application extends Once {
    constructor(name) {
        super();

        this.name = name;
    }
}

Тест:

console.log(new Once() === new Once());

let app1 = new Application('Foobar');
let app2 = new Application('Barfoo');

console.log(app1 === app2);
console.log(app1.name); // Barfoo
frasq
источник
1

Если вы хотите использовать классы:

class Singleton {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    if(this.constructor.instance)
      return this.constructor.instance;
    this.constructor.instance = this;
  }
}
let x = new Singleton('s',1);
let y = new Singleton('k',2);

Выход для вышеупомянутого будет:

console.log(x.name,x.age,y.name,y.age) // s 1 s 1

Другой способ написания синглтона с использованием функции

function AnotherSingleton (name,age) {
  this.name = name;
  this.age = age;
  if(this.constructor.instance)
    return this.constructor.instance;
  this.constructor.instance = this;
}

let a = new AnotherSingleton('s',1);
let b = new AnotherSingleton('k',2);

Выход для вышеупомянутого будет:

console.log(a.name,a.age,b.name,b.age)// s 1 s 1
sudhee
источник