Maneira mais simples / mais limpa de implementar singleton em JavaScript?

300

Qual é a maneira mais simples / limpa de implementar o padrão singleton em JavaScript?

Jakub Arnold
fonte
15
Voto negativo para a resposta aceita que não é um singleton. É apenas uma variável global.
mlibby
5
Essa é uma tonelada de informações, mas realmente estabelece as diferenças entre os diferentes padrões de design do JS. Ele me ajudou muito: addyosmani.com/resources/essentialjsdesignpatterns/book
Justin

Respostas:

315

Eu acho que a maneira mais fácil é declarar um simples objeto literal:

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

Se você deseja membros privados em sua instância singleton, pode fazer algo assim:

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

  function privateMethod () {
    // ...
  }

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

Isto tem sido chamado o padrão de módulo , que basicamente permite que você encapsular membros privados em um objeto, aproveitando-se o uso de fechamentos .

UPDATE: Gostaria de acrescentar que, se você quiser impedir a modificação do objeto singleton, poderá congelá-lo usando o ES5Object.freeze método .

Isso tornará o objeto imutável, impedindo qualquer modificação em sua estrutura e valores.

Além disso, gostaria de mencionar que, se você estiver usando o ES6, poderá representar um singleton usando os Módulos do ES com muita facilidade e pode manter um estado privado declarando variáveis ​​no escopo do módulo :

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

function privateFn () {
  // ...
}

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

Então você pode simplesmente importar o objeto singleton para usá-lo:

import myInstance from './my-singleton.js'
// ...
CMS
fonte
46
+1 Não é um pouco estranho procurar um "padrão Singleton" em um idioma com variáveis ​​globais ???
Victor
4
Usando o padrão do módulo, como um membro público acessaria outro membro público? Ou seja, como publicMethod1chamaria publicMethod2?
typeof
4
@ Tom, sim, o padrão nasceu em linguagens OOP baseadas em classe - lembro-me de muitas implementações que envolviam um getInstancemétodo estático e um construtor privado -, mas IMO, essa é a maneira mais "simples" de criar um objeto singleton em Javascript, e no final ele atende ao mesmo objetivo - um único objeto, que você não pode inicializar novamente (não há construtor, é apenas um objeto) -. Sobre o código vinculado, ele tem alguns problemas, trocar as ae bvariável declarações e teste a === window. Felicidades.
CMS
15
@ Victor - Não é estranho procurar um "padrão singleton" nesse idioma. muitas linguagens orientadas a objetos utilizam variáveis ​​globais e ainda singletons estão em uso. Singleton não é apenas garantia de que haverá apenas um objeto de determinada classe. O Singleton possui mais alguns recursos: 1) deve ser inicializado no primeiro uso (o que não significa apenas inicialização atrasada, mas também garantir que o objeto esteja realmente pronto para ser usado) 2) deve ser seguro para threads. O padrão do módulo pode substituir o padrão singleton, mas apenas no navegador (e nem sempre).
skalee
55
Esta não deve ser a resposta aceita. Este não é um singleton! Esta é apenas uma variável global. Há um mundo de diferença entre os dois.
mlibby
172

Eu acho que a abordagem mais limpa é algo como:

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

Depois, você pode chamar a função como

var test = SingletonFactory.getInstance();
sebarmeli
fonte
4
Observação: o construtor original pode ser lido novamente usando delete instance.constructor:x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Rob W
var test = SingletonClass.getInstance () - não parece muito limpo ou como o JS. Outras almas que terminam com a = new Foo (); b = novo Foo (); a === b // true
Matthias
3
Cheira mais como fábrica para mim com toda a parte "getInstance".
Lajos Meszaros
1
Este não é um singleton porque você pode criar várias instâncias com Object.create.
AndroidDev 13/01/2017
Fiddle é quebrado
HoffZ
103

Não tenho certeza se concordo com o padrão do módulo sendo usado como substituto de um padrão singleton. Eu sempre vi singletons usados ​​e abusados ​​em lugares onde eles são totalmente desnecessários, e tenho certeza que o padrão do módulo preenche muitas lacunas onde os programadores usariam um singleton, mas o padrão do módulo não é um singleton.

padrão do módulo:

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

Tudo inicializado no padrão do módulo acontece quando Foo é declarado. Além disso, o padrão do módulo pode ser usado para inicializar um construtor, que pode ser instanciado várias vezes. Embora o padrão do módulo seja a ferramenta certa para muitos trabalhos, não é equivalente a um singleton.

padrão singleton:

forma curta
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();
}
formato longo, usando o padrão do módulo
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;
}());

Nas duas versões do padrão Singleton que eu forneci, o próprio construtor pode ser usado como o acessador:

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

Se você não se sentir confortável usando o construtor dessa maneira, poderá gerar um erro na if (instance)instrução e continuar usando o formulário longo:

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

Também devo mencionar que o padrão singleton se ajusta bem ao padrão implícito da função construtora:

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
fonte
4
Eu nunca disse nada sobre singletons sendo uma idéia ruim ou boa. Eu disse que sua implementação do singleton é muito mais complicada do que precisa, porque você confunde as limitações do Java com o padrão como se não o entendesse. É como implementar o padrão de estratégia criando funções e métodos de construtor quando você pode simplesmente usar funções anônimas para isso em Javascript.
Esailija 17/11/2013
11
@ Esailija, parece-me que você não entende o padrão singleton. O padrão singleton é um padrão de design que restringe a instanciação de uma classe a um objeto. var singleton = {}não se encaixa nessa definição.
zzzzBov
9
Isso se aplica apenas a linguagens como Java por causa de suas limitações. var singleton = {}é como você implementa o singleton em Javascript .
Esailija 17/11/2013
2
@Esailija, "Em uma linguagem de programação baseada em protótipo, onde objetos, mas não classes, são usados ​​..." JavaScript tem o conceito de classes, de modo que isso não se aplica.
zzzzBov
18

Em 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
fonte
1
O congelamento faria sentido se Singleton fosse um invólucro em torno de alguma outra classe e tivesse apenas o instancecampo. Como está atualmente ( instancedefinida como this), essa classe também pode ter outros campos e o congelamento não faz sentido.
Thisismydesign
10

O seguinte funciona no nó v6

class Foo {
  constructor(msg) {

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

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

Testamos:

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

No ES6, a maneira correta de fazer isso é:

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

Ou, se você não deseja que um erro seja gerado na criação da segunda instância, basta retornar a última instância, assim:

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
fonte
Oi, você pode me ajudar a saber a diferença entre _instance e instância como eu estava usando instância e código não estava funcionando
Abhinav bhardwaj
Não há diferença técnica em instancee _instance. É apenas uma convenção de nomenclatura nas linguagens de programação que denominamos variáveis privadas anexadas com um sublinhado. Eu suspeito que a razão para o seu código não funcionar é que você está usando em this.instancevez deMyClass.instance
UtkarshPramodGupta
7

Existem mais de uma maneira de esfolar um gato :) Dependendo do seu gosto ou necessidade específica, você pode aplicar qualquer uma das soluções propostas. Pessoalmente, busco a primeira solução do CMS sempre que possível (quando você não precisa de privacidade). Como a pergunta era sobre a mais simples e a mais limpa, esse é o vencedor. Ou até:

var myInstance = {}; // done!

Isso (citação do meu blog) ...

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

não faz muito sentido (o exemplo do meu blog também não) porque não precisa de vars particulares, portanto é praticamente o mesmo que:

var SingletonClass = { 
    myFunction: function () { 
        //do stuff 
    },
    instance: 1 
}
Stoyan
fonte
O snippet de código 2 contém um erro de sintaxe. Você não pode escreverthis.f(){}
xoxox
7

Eu reprovo minha resposta, vejo minha outra .

Normalmente, o padrão do módulo (consulte a resposta do CMS), que NÃO é padrão singleton, é bom o suficiente. No entanto, um dos recursos do singleton é que sua inicialização é atrasada até que o objeto seja necessário. O padrão do módulo não possui esse recurso.

Minha proposta (CoffeeScript):

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

O que foi compilado em JavaScript:

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

Então eu posso fazer o seguinte:

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
fonte
Por que você carregaria o módulo se não for necessário? E quando você precisa carregar o módulo, carrega o módulo e ele é inicializado.
Esailija
6

Resposta curta:

Devido à natureza não-bloqueadora do JavaScript, Singletons em JavaScript são realmente feios em uso. As variáveis ​​globais também fornecerão uma instância por todo o aplicativo, sem todos esses retornos de chamada; o padrão do módulo oculta suavemente os internos por trás da interface. Consulte a resposta @CMS.

Mas, desde que você queria um singleton ...

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

};

Uso:

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

Explicação:

Os singletons oferecem mais do que apenas uma instância em todo o aplicativo: a inicialização é adiada até o primeiro uso. Isso é realmente importante quando você lida com objetos cuja inicialização é cara. Caro geralmente significa E / S e, em JavaScript, E / S sempre significam retornos de chamada.

Não confie em respostas que lhe dão uma interface instance = singleton.getInstance(), pois todas elas não entendem.

Se eles não aceitarem que o retorno de chamada seja executado quando a instância estiver pronta, eles não funcionarão quando o inicializador fizer E / S.

Sim, os retornos de chamada sempre parecem mais feios que a chamada de função, que retorna imediatamente a instância do objeto. Mas, novamente: quando você faz E / S, os retornos de chamada são obrigatórios. Se você não deseja fazer nenhuma E / S, a instanciação é barata o suficiente para fazê-lo no início do programa.

Exemplo 1, inicializador barato:

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

Exemplo 2, inicialização com E / S:

Neste exemplo, setTimeoutfalsifica algumas operações caras de E / S. Isso ilustra por que os singletons em JavaScript realmente precisam de retornos de chamada.

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
fonte
Por meio de tentativas e tribulações com outras respostas únicas que não foram suficientes, eu cheguei ao código resultante notavelmente semelhante a isso.
precisa saber é o seguinte
Por uma razão ou outra, esta é a única resposta que faz sentido para mim. Todas as outras respostas me lembram o episódio do goon show, em que três homens tentam escalar um muro com quatro pessoas de altura, subindo nos ombros um do outro recursivamente.
Tim Ogilvy
O empilhamento de costas é o que eu realmente precisava! Obrigado!!
Tim Ogilvy
Na verdade, essa abordagem nunca fornece um singleton como: singleton (singletonInitializer)! == singleton (singletonInitializer) são duas instâncias diferentes. A função resultante que você retornou pode ser usada para anexar mais retornos de chamada à instância, mas não especifica estritamente que apenas uma instância desse tipo pode ser criada. Qual é o objetivo de um singleton.
Owen
6

Eu peguei este exemplo no JavaScript Patterns Crie aplicativos melhores com padrões de codificação e design Pelo livro de Stoyan Stefanov , caso você precise de uma classe de implementação simples, como o objeto singltone, use a função imediata da seguinte maneira:

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

E você pode verificar este exemplo seguindo o caso de teste:

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

Essa abordagem passa em todos os casos de teste, enquanto a implementação estática privada falha quando a extensão do protótipo é usada (pode ser corrigida, mas não será simples), e a implementação estática pública menos aconselhável, porque a instância é exposta ao público.

Demonstração do jsFiddly.

Khamidulla
fonte
5

Acho que encontrei a maneira mais limpa de programar em JavaScript, mas você precisará de imaginação. Eu recebi essa idéia de uma técnica de trabalho no livro "javascript the good parts".

Em vez de usar a nova palavra-chave, você pode criar uma classe como esta:

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

Você pode instanciar o objeto acima dizendo:

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

Agora, com este método de trabalho em mente, você pode criar um singleton como este:

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

}();

Agora você pode obter sua instância chamando

var obj = ClassSingleton.getInstance();

Eu acho que essa é a maneira mais legal, pois a "Classe" completa nem sequer é acessível.

David
fonte
Mas com essa técnica, você pode ter mais de uma instância. Isso não está certo.
Nicolascolman
1
Eu acho que não, você não pode acessar a classe sem passar pelo getInstance. Você poderia elaborar?
David
David Maes Desculpe, mas eu não notei a validação no segundo exemplo. Peço desculpas.
22617 nicolascolman #
4

O @CMS e o @zzzzBov deram respostas maravilhosas, mas apenas para adicionar minha própria interpretação, com base na minha mudança para o desenvolvimento node.js pesado do PHP / Zend Framework, onde os padrões singleton eram comuns.

O seguinte código documentado para comentários é baseado nos seguintes requisitos:

  • uma e apenas uma instância do objeto de função pode ser instanciada
  • a instância não está disponível ao público e só pode ser acessada por meio de um método público
  • o construtor não está disponível ao público e só pode ser instanciado se ainda não houver uma instância disponível
  • a declaração do construtor deve permitir que sua cadeia de protótipos seja modificada. Isso permitirá que o construtor herde de outros protótipos e ofereça métodos "públicos" para a instância

Meu código é muito semelhante ao do @ zzzzBov, exceto que eu adicionei uma cadeia de protótipos ao construtor e mais comentários que devem ajudar os que vêm do PHP ou de uma linguagem semelhante a traduzir a natureza prototípica do OOP tradicional para Javascripts. Pode não ser o "mais simples", mas acredito que seja o mais adequado.

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

Observe que tecnicamente, a função anônima de execução automática é, ela própria, um Singleton, conforme demonstrado com agrado no código fornecido pelo @CMS. O único problema aqui é que não é possível modificar a cadeia de protótipos do construtor quando o próprio construtor é anônimo.

Lembre-se de que, para Javascript, os conceitos de "público" e "privado" não se aplicam como em PHP ou Java. Mas alcançamos o mesmo efeito, aproveitando as regras de disponibilidade de escopo funcional do Javascript.

talentmrjones
fonte
Várias instâncias podem ser criadas a partir de seu código:var a = Singleton.getInstance('foo'); var b = new a.constructor('bar');
zzzzBov
@zzzzBov: Eu só estou recebendo erros tentando isso no meu violino: jsfiddle.net/rxMu8
cincodenada
4

Não sei por que ninguém trouxe isso à tona, mas você pode fazer:

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

  this.foo = function() {
    // whatever
  }
})()
Derek Chiang
fonte
Essa parece ser uma maneira interessante de ignorar o método getInstance e obter uma solução mais simples. Mas tenha em mente que o singleton será executado assim que o arquivo é analisado, o que significa que os ouvintes DOM deve ser envolto em um (documento) $ função .ready
HoffZ
4

A resposta mais clara deve ser essa do livro Learning JavaScript Design Patterns de Addy Osmani.

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

令狐 葱
fonte
3

Acredito que esta é a maneira mais simples / limpa e mais intuitiva, embora exija ES7:

export default class Singleton {

  static instance;

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

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

}

O código fonte é de: adam-bien.com

Alt Eisen
fonte
Isso está completamente errado e geraria erro na chamadanew Singleton()
UtkarshPramodGupta 20/01
2

O que há de errado nisso?

function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}
Manav
fonte
Test = Klass; t1 = new Test(); t2 = new Test();- sem oportunidade de renomear a classe ou escolher um espaço para nome diferente.
precisa
2

posso colocar minhas 5 moedas? Eu tenho uma função construtora, ex.

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

O que eu preciso fazer é que todos os objetos criados por esse CF sejam iguais.

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

teste

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

Eu achei o seguinte o padrão Singleton mais fácil, porque o uso do novo operador torna isso imediatamente disponível na função, eliminando a necessidade de retornar um literal de objeto:

var singleton = new (function () {

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

singleton.printSomething();

Marca
fonte
2

Aqui está o exemplo simples para explicar o padrão singleton em 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();
Sheo Dayal Singh
fonte
1

Eu precisava de vários singletons com:

  • inicialização preguiçosa
  • parâmetros iniciais

e foi assim que eu inventei:

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 deve ser Array para que isso funcione; se você tiver variáveis ​​vazias, basta passar []

  • Usei o objeto window na função, mas poderia ter passado um parâmetro para criar meu próprio escopo

  • Os parâmetros name e construct são apenas String para que window [] funcione, mas com algumas verificações simples, window.name e window.construct também são possíveis.

fred
fonte
1

Que tal dessa maneira, apenas garantir que a classe não possa ser nova novamente.

Por isso, você pode usar o instanceofop, também, você pode usar a cadeia de protótipos para herdar a classe, é uma classe regular, mas não pode ser nova, se você deseja obter a instância, basta usargetInstance

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

Se você não deseja expor o instancemembro, basta encerrá-lo.

Wener
fonte
1

A seguir, é apresentado o trecho do meu percurso para implementar um padrão singleton. Isso me ocorreu durante um processo de entrevista e achei que deveria capturar isso em algum lugar.

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

o mesmo pode ser encontrado em minha essência página

curioso
fonte
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
Paul Sweatte
fonte
1

Isso não é um singleton também?

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();
Nawal
fonte
1

Você pode fazer isso com decoradores, como neste exemplo abaixo, para TypeScript:

class YourClass {

    @Singleton static singleton() {}

}

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

Então você usa seu singleton assim:

var myInstance = YourClass.singleton();

No momento da redação deste artigo, os decoradores não estão prontamente disponíveis nos mecanismos JavaScript. Você precisa garantir que o tempo de execução do JavaScript tenha os decoradores realmente ativados ou use compiladores como Babel e TypeScript.

Observe também que a instância singleton é criada "preguiçosa", ou seja, é criada somente quando você a utiliza pela primeira vez.

Vad
fonte
1

Padrão do módulo: no "estilo mais legível". Você pode ver facilmente quais métodos são públicos e quais são privados

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

agora você pode usar métodos públicos como

module.method2 (); // -> Estou chamando um método privado por um alerta de método público ("oi, sou um método privado")

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

ncubica
fonte
1

Singleton:

Garanta que uma classe tenha apenas uma instância e forneça um ponto de acesso global a ela.

O Padrão Singleton limita o número de instâncias de um objeto específico a apenas uma. Essa instância única é chamada de singleton.

  • define getInstance () que retorna a instância exclusiva.
  • responsável por criar e gerenciar o objeto de instância.

O objeto Singleton é implementado como uma função anônima imediata. A função é executada imediatamente envolvendo-a entre colchetes, seguida por dois colchetes adicionais. É chamado anônimo porque não tem nome.

Programa de amostra,

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

Mohideen bin Mohammed
fonte
1

Mais simples / mais limpo para mim significa também simplesmente entender e não há sinos e assobios, como são discutidos na versão Java da discussão:

O que é uma maneira eficiente de implementar um padrão singleton em Java?

A resposta que se encaixaria melhor / mais limpa do meu ponto de vista é:

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

E só pode ser traduzido parcialmente para JavaScript. Algumas das diferenças em Javascript são:

  • construtores não podem ser privados
  • Classes não podem ter campos declarados

Mas, dada a sintaxe mais recente da ECMA, é possível se aproximar de:

Padrão Singleton como exemplo de classe 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;
  }
}

Exemplo de uso

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

Resultado de exemplo

DefaultField1
DefaultField2
Wolfgang Fahl
fonte
1
function Once() {
    return this.constructor.instance || (this.constructor.instance = this);
}

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

    app.name = name;

    return app;
}

Se você estiver em aulas:

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

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

        this.name = name;
    }
}

Teste:

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
fonte
1

Se você deseja usar classes:

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

A saída para o acima será:

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

Outra maneira de escrever Singleton usando a função

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

A saída para o acima será:

console.log(a.name,a.age,b.name,b.age)// s 1 s 1
sudhee
fonte