Najprostszy/najczystszy sposób implementacji Singletona w JavaScript

Jaki jest najprostszy/najczystszy sposób implementacji singleton pattern w JavaScript?

Author: Peter Mortensen, 2009-09-25

30 answers

Myślę, że najprostszym sposobem jest zadeklarowanie prostego obiektu literalnie:

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

Jeśli chcesz mieć prywatnych członków na swojej instancji singleton, możesz zrobić coś takiego:

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

  function privateMethod () {
    // ...
  }

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

To się nazywa wzór modułu, i zasadniczo pozwala na hermetyzację prywatnych członków na obiekcie, korzystając z użycia zamknięć.

Jeśli chcesz zapobiec modyfikacji obiektu singleton, możesz zamrozić go, używając ES5 Object.freeze metoda.

To uczyni obiekt niezmiennym, uniemożliwiając modyfikację jego struktury i wartości.

Jeśli używasz ES6, możesz reprezentować singleton używając modułów ES bardzo łatwo, a nawet możesz trzymać Stan prywatny deklarując zmienne w zakres modułu :

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

function privateFn () {
  // ...
}

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

Następnie możesz po prostu zaimportować obiekt singleton, aby go użyć:

import myInstance from './my-singleton.js'
// ...
 328
Author: Christian C. Salvadó,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 21:18:58

Myślę, że najczystsze podejście to coś w stylu:

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

Następnie możesz wywołać funkcję jako

var test = SingletonFactory.getInstance();
 179
Author: sebarmeli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-01 15:44:32

Nie jestem pewien, czy Zgadzam się ze wzorem modułu używanym jako zamiennik wzoru Singletona. Często widziałem singletony używane i nadużywane w miejscach, gdzie są całkowicie niepotrzebne, i jestem pewien, że wzór modułu wypełnia wiele luk, gdzie programiści inaczej używać singleton. Jednak wzór modułu to , a nie singleton.

Wzór modułu:

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

Wszystko zainicjalizowane we wzorze modułu dzieje się, gdy Foo jest zadeklarowane. Dodatkowo moduł wzorzec może być użyty do zainicjowania konstruktora, który następnie może być wielokrotnie inicjowany. Chociaż wzorzec modułu jest właściwym narzędziem dla wielu zadań, nie jest odpowiednikiem Singletona.

Wzór Singletona:

krótka forma
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();
}
long form, using module pattern
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;
}());

W obu wersjach wzoru Singletona, który podałem, sam konstruktor może być użyty jako accessor:

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

Jeśli nie czujesz się komfortowo używając konstruktora to w ten sposób możesz rzucić błąd w instrukcji if (instance) i trzymać się długiego formularza:

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

Powinienem również wspomnieć, że wzór Singletona dobrze pasuje do niejawnego wzoru funkcji konstruktora:

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
 109
Author: zzzzBov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 21:42:02

W ES6 dobrym sposobem na to jest:

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

Lub, jeśli nie chcesz, aby błąd został wyrzucony podczas tworzenia drugiej instancji, możesz po prostu zwrócić ostatnią instancję, w następujący sposób:

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"
 28
Author: UtkarshPramodGupta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:34:35

In ECMAScript 2015 (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
 21
Author: Xaqron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:21:08

Następujące działania w Node.js wersja 6:

class Foo {
  constructor(msg) {

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

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

Testujemy:

const f = new Foo('blah');
const d = new Foo('nope');
console.log(f); // => Foo { msg: 'blah' }
console.log(d); // => Foo { msg: 'blah' }
 13
Author: Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:28:56

Jest więcej niż jeden sposób na oskórowanie kota:) w zależności od gustu lub specyficznej potrzeby można zastosować dowolne z proponowanych rozwiązań. Osobiście wybieram pierwsze rozwiązanie Christiana C. Salvadó Kiedy tylko jest to możliwe (kiedy nie potrzebujesz prywatności).

Ponieważ pytanie było najprostsze i najczystsze, to jest zwycięzca. Albo nawet:
var myInstance = {}; // Done!

To (cytat z mojego bloga)...

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

Nie ma większego sensu (mój przykład na blogu też nie ma), ponieważ nie potrzebuje żadnych zmienne prywatne, więc jest to prawie to samo co:

var SingletonClass = {
    myFunction: function () {
        // Do stuff
    },
    instance: 1
}
 7
Author: Stoyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 21:36:14

Odrzucam moją odpowiedź, patrz moja druga .

Zazwyczaj wzór modułu (zobacz odpowiedź Christiana C. Salvadó ), który jest , a nie wzór Singletona jest wystarczająco dobry. Jednak jedną z cech Singletona jest to, że jego inicjalizacja jest opóźniona do momentu, gdy obiekt jest potrzebny. Wzorzec modułu nie posiada tej funkcji.

Moja propozycja (CoffeeScript):

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

Który skompilował to w JavaScript:

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

Then I can do "po": {]}

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
Author: skalee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 21:37:17

Krótka odpowiedź:

Ze względu na nieblokującą naturę JavaScript, singletony w JavaScript są naprawdę brzydkie w użyciu. Zmienne globalne dadzą ci jedno wystąpienie przez całą aplikację również bez wszystkich tych wywołań zwrotnych, a wzór modułu delikatnie ukrywa wewnętrzne elementy za interfejsem. Zobacz odpowiedź Christiana C. Salvadó .

Ale skoro chciałeś Singletona...]}
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);
    }
  };

};

Użycie:

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

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

Wyjaśnienie:

Singletony dają Ci więcej niż jedną wystąpienie przez całą aplikację: ich inicjalizacja jest opóźniona do pierwszego użycia. Jest to naprawdę duża rzecz, gdy masz do czynienia z obiektami, których inicjalizacja jest kosztowna. Kosztowne zwykle oznacza I/O, a w JavaScript I / o zawsze oznacza wywołania zwrotne.

Nie ufaj odpowiedziom, które dają Ci interfejs jak instance = singleton.getInstance(), wszystkie one nie mają sensu.

Jeśli nie wywołują wywołania zwrotnego, gdy instancja jest gotowa, nie będą działać, gdy inicjalizator to zrobi I / O.

Tak, wywołania zwrotne zawsze wyglądają brzydiej niż wywołanie funkcji, które natychmiast zwraca instancję obiektu. Ale znowu: kiedy robisz I / O, wywołania zwrotne są obowiązkowe. Jeśli nie chcesz wykonywać żadnych operacji wejścia / Wyjścia, instancjacja jest na tyle tania, aby zrobić to przy starcie programu.

Przykład 1, cheap initializer:

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

Przykład 2, inicjalizacja za pomocą I/O:

W tym przykładzie, setTimeout udaje jakąś kosztowną operację wejścia/Wyjścia. To ilustruje, dlaczego singletony w JavaScript naprawdę potrzebują rozmowy zwrotne.

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.");
 6
Author: skalee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 22:38:29

Mam ten przykład z * wzorców JavaScript Tworzenie lepszych aplikacji z kodowania i wzorców projektowych książka (Stoyan Stefanov). W przypadku, gdy potrzebujesz jakiejś prostej klasy implementacyjnej, takiej jak obiekt singleton, możesz użyć natychmiastowej funkcji, jak w poniższej instrukcji:

var ClassName;

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

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

I możesz sprawdzić ten przykład, wykonując następujący przypadek testowy:

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

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

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

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

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

// Changing property value
obj_1.someProperty = 1;

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

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

To podejście przechodzi wszystkie przypadki testowe, podczas gdy prywatna statyczna implementacja zawiedzie, gdy używane jest rozszerzenie prototypu (może to być fixed, ale nie będzie to proste) i publiczna statyczna implementacja mniej zalecana ze względu na instancję jest wystawiona na publiczne działanie.

Jsfiddly demo.

 6
Author: Khamidulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 22:50:44

Christian C. Salvadó'S izzzzbov' s answer Dali wspaniałe odpowiedzi, ale po prostu dodać moją własną interpretację opartą na moim przejściu do ciężkiego węzła.js development z PHP / Zend Framework, gdzie wzorce Singletona były powszechne.

Poniższy, udokumentowany komentarzem kod opiera się na następujących wymaganiach:

  • jedna i tylko jedna instancja obiektu function może być utworzona
  • instancja nie jest publicznie dostępna i może dostęp można uzyskać tylko za pomocą metody publicznej
  • konstruktor nie jest publicznie dostępny i może być utworzony tylko wtedy, gdy nie ma jeszcze dostępnej instancji
  • deklaracja konstruktora musi umożliwiać modyfikację łańcucha prototypów. Pozwala to konstruktorowi dziedziczyć po innych prototypach i oferować metody" publiczne " dla instancji

Mój kod jest bardzo podobny do odpowiedzi zzzzbova, tylko że dodałem łańcuch prototypów do konstruktora i więcej komentarzy, które powinny pomóc osobom pochodzącym z PHP lub podobnego języka przetłumaczyć tradycyjny OOP na prototypowy charakter JavaScript. Może nie jest "najprostszy" , ale uważam, że jest najbardziej właściwy.

// Declare 'Singleton' as the returned value of a self-executing anonymous function
var Singleton = (function () {
    "use strict";
    // 'instance' and 'constructor' should not be available 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'

Zauważ, że technicznie, samo-wykonująca się funkcja anonimowa jest sama w sobie singletonem, co ładnie pokazano w kodzie dostarczonym przez Christiana C. Salvadó. Jedynym haczykiem jest to, że nie jest możliwe zmodyfikowanie łańcucha prototypów konstruktora, gdy sam konstruktor jest anonimowy.

Należy pamiętać, że w JavaScript pojęcia "publiczny" i "prywatny" nie mają zastosowania tak jak w PHP czy Javie. Ale osiągnęliśmy ten sam efekt, wykorzystując Zasady JavaScript dotyczące dostępności zakresu funkcjonalnego.

 5
Author: talentedmrjones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 22:03:19

Myślę, że znalazłem najczystszy sposób na programowanie w JavaScript, ale będziesz potrzebował trochę wyobraźni. Mam ten pomysł z pracy techniki w książce JavaScript: dobre Części.

Zamiast używać słowa kluczowego new , możesz utworzyć klasę taką jak:

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

    var privateVar;
    obj.publicVar;

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

    function privateMethod(){}

    return obj;
}

Możesz utworzyć instancję powyższego obiektu, mówiąc:

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

Teraz mając na uwadze tę metodę pracy, możesz utworzyć singleton w ten sposób:

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

Teraz możesz uzyskać swoją instancję przez wywołanie

var obj = ClassSingleton.getInstance();

Myślę, że jest to najbardziej schludny sposób, ponieważ kompletna "Klasa" nie jest nawet dostępna.

 5
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 22:59:34

Możesz po prostu zrobić:

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

  this.foo = function() {
    // Whatever
  }
})()
 4
Author: Derek Chiang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 22:08:05

Najjaśniejszą odpowiedzią powinna być ta z książki Learning JavaScript Design Patterns autorstwa 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 = "I'm 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;
    }

  };

})();
 4
Author: 令狐葱,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:28:14

To powinno zadziałać:

function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}
 3
Author: Manav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 21:57:21

Uważam, że jest to najprostszy / najczystszy i najbardziej intuicyjny sposób, chociaż wymaga ECMAScript 2016 (ES7):

export default class Singleton {

  static instance;

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

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

}

Kod źródłowy pochodzi z: adam-bien.com

 3
Author: Alt Eisen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:20:08

Poniżej znajduje się fragment z mojego przeglądu, aby zaimplementować wzór Singletona. Przyszło mi to do głowy podczas wywiadu i poczułem, że powinienem to gdzieś uchwycić.

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

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


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

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


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

  return Logger.log_instance;
};


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

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


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

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

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

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

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

To samo można znaleźć na mojej stronie gist .

 2
Author: curioussam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 22:24:27

Moje dwa grosze: mam funkcję konstruktora (CF), na przykład

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

Każdy obiekt stworzony przez ten CF musi być taki sam.

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

Test

var x1 = new X();
var x2 = new X();
console.log(x1 === x2)
 2
Author: Olencha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:04:21

Oto prosty przykład do wyjaśnienia wzorca Singletona w JavaScript.

var Singleton = (function() {
    var instance;
    var init = function() {
        return {
            display:function() {
                alert("This is a singleton pattern 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 pattern demo");
// It means one object is created

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

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

var inst1 = Singleton.getInstance();
inst1.display();
 2
Author: Sheo Dayal Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:26:22

Znalazłem następujący najprostszy wzór Singletona, ponieważ użycie nowego operatora sprawia, że ten jest natychmiast dostępny w funkcji, eliminując potrzebę zwracania obiektu literalnie:

var singleton = new (function () {

  var private = "A private value";

  this.printSomething = function() {
      console.log(private);
  }
})();

singleton.printSomething();
 2
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:29:34

Najprostszy/najczystszy dla mnie oznacza również po prostu zrozumienie i brak dzwonków i gwizdków, co jest dużo dyskutowane w wersji Java dyskusji:

jaki jest skuteczny sposób implementacji wzorca Singletona w Javie?

Odpowiedź, która najlepiej pasowałaby do najprostszych/najczystszych z mojego punktu widzenia to:

Odpowiedź Jonathana na Jaki jest skuteczny sposób implementacji wzorca Singletona w Javie?

I może być tylko częściowo przetłumaczone na język JavaScript. Niektóre z różnic w JavaScript to:

  • konstruktorzy nie mogą być prywatni
  • klasy nie mogą mieć zadeklarowanych pól

Ale biorąc pod uwagę najnowszą składnię ECMA, można zbliżyć się do:

Wzór Singletona jako przykład klasy 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;
  }
}

Przykład Użycia

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

Przykładowy Wynik

DefaultField1
DefaultField2
 2
Author: Wolfgang Fahl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:31:51

Dla mnie najczystszym sposobem jest:

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

Dzięki tej składni jesteś pewien, że Twój singleton jest i pozostanie unikalny. Możesz również cieszyć się cukrem składni klas i używać this zgodnie z oczekiwaniami.

(zauważ, że pola klasy wymagają node v12+ lub nowoczesnej przeglądarki.)

 2
Author: Lepzulnag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:33:37

Jeśli chcesz użyć klas:

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

Wyjście dla powyższego będzie:

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

Inny sposób zapisu Singletonu za pomocą funkcji

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

Wyjście dla powyższego będzie:

console.log(a.name, a.age, b.name, b.age) // s 1 s 1
 2
Author: sudhee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:38:58
function Once() {
    return this.constructor.instance || (this.constructor.instance = this);
}

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

    app.name = name;

    return app;
}

Jeśli jesteś w klasach:

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

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

        this.name = name;
    }
}

Test:

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
 1
Author: frasq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-22 23:20:15

Potrzebowałem kilku singletonów z:

  • leniwa inicjalizacja
  • parametry początkowe

I tak to wymyśliłem:

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 musi być tablica, aby to działało, więc jeśli masz puste zmienne, po prostu podaj []

  • Użyłem obiektu window w funkcji, ale mogłem przekazać parametr, aby utworzyć własny zakres

  • Nazwa i parametry konstruktu są tylko ciągiem znaków w kolejności dla okienko[] do pracy, ale z prostym typecheckingiem, window.name i okno.construct są również możliwe.

 1
Author: fred,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 21:56:38

Wzór modułu: w "bardziej czytelnym stylu". Możesz łatwo zobaczyć, które metody są publiczne, a które prywatne

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

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

   function imaprivatemethod(){
     alert("Hi, I'm a private method");
   }

   /* Public Methods & variables */

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

   $r.methdo1 = function(){
       console.log("method1 calls 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 the init method

   return $r; // Returning all public methods

})("module");

Teraz możesz używać publicznych metod, takich jak

Moduł.method2 (); / / - > wywołuję prywatną metodę nad publicznym alertem metody("Cześć, jestem prywatną metodą")

Http://jsfiddle.net/ncubica/xMwS9/

 1
Author: ncubica,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 22:07:20

Inny sposób-po prostu ubezpieczyć klasę nie może znowu nowy.

W ten sposób możesz użyć operacji instanceof. Możesz również użyć łańcucha prototypów do dziedziczenia klasy. To zwykłe zajęcia, ale nie można Nowe to. Jeśli chcesz uzyskać instancję, po prostu użyj 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');}
}

// Initialise the instance
new CA();

// Test here
var c = CA.getInstance()
c.func();
console.assert(c instanceof CA)

// This will fail
var b = new CA();

Jeśli nie chcesz ujawnić członka instance, po prostu Zakończ to.

 1
Author: wener,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 22:13:18
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,
    // and then pad zeroes to the code point
    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
Author: Paul Sweatte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 22:15:36

To jest też singleton:

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
Author: Nawal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:01:05

Możesz to zrobić za pomocą dekoratorów, jak w poniższym przykładzie dla maszynopisu:

class YourClass {

    @Singleton static singleton() {}

}

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

Potem używasz swojego Singletona w ten sposób:

var myInstance = YourClass.singleton();

W momencie pisania tego tekstu, dekoratory nie są łatwo dostępne w silnikach JavaScript. Musisz upewnić się, że środowisko uruchomieniowe JavaScript ma włączone dekoratory lub używać kompilatorów, takich jak Babel i TypeScript.

Zauważ również, że instancja Singletona jest tworzona "leniwie", tzn. jest tworzona tylko wtedy, gdy użyjesz jej po raz pierwszy.

 1
Author: Vad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-19 23:08:52