Najprostszy / najczystszy sposób na wdrożenie singletona w JavaScript?

300

Jaki jest najprostszy / najczystszy sposób zaimplementowania wzorca singleton w JavaScript?

Jakub Arnold
źródło
15
Głosuj za zaakceptowaną odpowiedzią, która wcale nie jest singletonem. To tylko zmienna globalna.
mlibby
5
Jest to mnóstwo informacji, ale naprawdę określa różnice między różnymi wzorami projektowymi JS. Bardzo mi pomogło: addyosmani.com/resources/essentialjsdesignpatterns/book
Justin

Odpowiedzi:

315

Myślę, że najłatwiej jest zadeklarować prosty dosłowny obiekt:

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

Jeśli chcesz członków prywatnych w 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 () {
    }
  };
})();

Nazywa się to wzorcem modułowym i zasadniczo pozwala enkapsulować prywatne elementy na obiekcie, wykorzystując zamknięcia .

AKTUALIZACJA: Chciałbym dodać, że jeśli chcesz zapobiec modyfikacji obiektu singletonu, możesz go zatrzymać , używając Object.freezemetody ES5 .

To sprawi, że obiekt będzie niezmienny, zapobiegając jakiejkolwiek modyfikacji jego struktury i wartości.

Dodatkowo chciałbym wspomnieć, że jeśli używasz ES6, możesz bardzo łatwo reprezentować singletona za pomocą ES Modules , a nawet możesz utrzymywać stan prywatny , deklarując zmienne w zakresie 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'
// ...
CMS
źródło
46
+1 Czy to nie jest trochę dziwne szukać „wzorca singletonu” w języku ze zmiennymi globalnymi ???
Victor,
4
Używając wzorca modułu, w jaki sposób członek publiczny uzyska dostęp do innego członka publicznego? Tj. Jak byś publicMethod1zadzwonił publicMethod2?
typeof
4
@Tom, tak, wzorzec narodził się w opartych na klasach językach OOP - pamiętam wiele implementacji, które obejmowały getInstancemetodę statyczną i prywatnego konstruktora - ale IMO to najprostszy sposób na zbudowanie obiektu singleton w JavaScript, a na koniec spełnia ten sam cel - pojedynczy obiekt, którego nie można ponownie zainicjować (nie ma konstruktora, to tylko obiekt) -. Jeśli chodzi o kod, który podłączyłeś, ma on pewne problemy, zamienia ai bdeklaruje zmienne i testuje a === window. Twoje zdrowie.
CMS
15
@Victor - Nie jest dziwne szukanie „wzorca singletonu” w takim języku. wiele języków zorientowanych obiektowo wykorzystuje zmienne globalne i wciąż są używane singletony. Singleton to nie tylko gwarancja, że ​​będzie tylko jeden obiekt danej klasy. Singleton ma jeszcze kilka funkcji: 1) powinien zostać zainicjowany przy pierwszym użyciu (co oznacza nie tylko opóźnioną inicjalizację, ale także gwarantuje, że obiekt jest naprawdę gotowy do użycia) 2) powinien być bezpieczny dla wątków. Wzorzec modułu może zastępować wzorzec singletonu, ale tylko w przeglądarce (i nie zawsze).
skalee
55
To nie powinna być zaakceptowana odpowiedź. To wcale nie jest singleton! To tylko zmienna globalna. Jest między nimi świat różnic.
mlibby
172

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

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ę as

var test = SingletonFactory.getInstance();
sebarmeli
źródło
4
Uwaga: Oryginalny konstruktor można odczytać ponownie, używając delete instance.constructor:x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Rob W
var test = SingletonClass.getInstance () - nie wygląda bardzo czysto lub podobnie jak JS. Inne dusze, które kończą się na = new Foo (); b = nowy Foo (); a === b // prawda
Matthias
3
Pachnie mi bardziej jak fabryka z całą częścią „getInstance”.
Lajos Meszaros
1
To nie jest singleton, ponieważ możesz utworzyć wiele jego wystąpień za pomocą Object.create.
AndroidDev,
Skrzypce
103

Nie jestem pewien, czy zgadzam się na użycie wzorca modułu jako zamiennika wzorca singletonu. Często widziałem singletony używane i nadużywane w miejscach, w których są całkowicie niepotrzebne, i jestem pewien, że wzór modułu wypełnia wiele luk, w których programiści używaliby singletonu, jednak wzór modułu nie jest singletonem.

wzór modułu:

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

Wszystko zainicjowane we wzorcu modułu dzieje się, gdy Foozostanie zadeklarowane. Ponadto wzorca modułu można użyć do zainicjowania konstruktora, który można następnie utworzyć wiele razy. Chociaż wzorzec modułu jest właściwym narzędziem do wielu zadań, nie jest równoważny z singletonem.

wzór singletonu:

skrócona 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();
}
długa forma, przy użyciu wzoru modułu
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 wzorca Singleton, który podałem, sam konstruktor może być używany jako akcesorium:

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

Jeśli nie czujesz się komfortowo przy użyciu konstruktora w ten sposób, możesz zgłosić błąd w if (instance)instrukcji i trzymać się długiej formy:

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

Powinienem również wspomnieć, że wzorzec singletonu dobrze pasuje do domyślnego wzorca 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
zzzzBov
źródło
4
Nigdy nie mówiłem nic o tym, że singletony są złym lub dobrym pomysłem. Powiedziałem, że twoja implementacja singletonu jest o wiele bardziej skomplikowana, niż musi być, ponieważ mylisz ograniczenia Javy ze wzorcem, jakbyś w ogóle go nie rozumiał. To jak implementacja wzorca strategii poprzez tworzenie funkcji i metod konstruktora, kiedy można po prostu użyć do tego anonimowych funkcji w JavaScript.
Esailija,
11
@Esailija, wydaje mi się, że nie rozumiesz wzoru singletonu. Wzorzec singletonu jest wzorcem projektowym, który ogranicza tworzenie wystąpienia klasy do jednego obiektu. var singleton = {}nie pasuje do tej definicji.
zzzzBov,
9
Dotyczy to tylko języków takich jak Java ze względu na ich ograniczenia. var singleton = {}jest jak zaimplementować singleton w JavaScript .
Esailija,
2
@Esailija, „W prototypowym języku programowania, w którym używane są obiekty, ale nie klasy ...” JavaScript ma pojęcie klas, więc nie ma zastosowania.
zzzzBov,
18

W 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
źródło
1
Zamrożenie miałoby sens, gdyby Singleton był jakaś inna klasa i miałaby tylko instancepole. Ponieważ jest obecnie ( instanceustawiona na this) ta klasa może mieć także inne pola, a zamrażanie nie ma sensu.
thisismydesign
10

Następujące działa w węźle v6

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' }
Daniel
źródło
8

W ES6 właściwym 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 był generowany 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"

UtkarshPramodGupta
źródło
Cześć, pomożecie mi poznać różnicę między _instance a instancją, ponieważ używałem instancji, a kod nie działał
Abhinav bhardwaj
Nie ma technicznej różnicy w instancei _instance. To tylko konwencja nazewnictwa w językach programowania, którą nazywamy zmiennymi prywatnymi poprzedzonymi znakiem podkreślenia. Podejrzewam, że przyczyną tego, że Twój kod nie działa, jest to, że używasz this.instancezamiastMyClass.instance
UtkarshPramodGupta
7

Jest więcej niż jeden sposób na skórowanie kota :) W zależności od gustu lub konkretnej potrzeby możesz zastosować dowolne z proponowanych rozwiązań. Osobiście wybieram pierwsze rozwiązanie CMS, gdy tylko jest to możliwe (gdy nie potrzebujesz prywatności). Ponieważ pytanie dotyczyło najprostszego i najczystszego, to jest zwycięzca. Lub 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), ponieważ nie potrzebuje żadnych prywatnych zmiennych, więc jest prawie taki sam jak:

var SingletonClass = { 
    myFunction: function () { 
        //do stuff 
    },
    instance: 1 
}
Stoyan
źródło
Fragment kodu 2 zawiera błąd składniowy. Nie możesz pisaćthis.f(){}
xoxox,
7

Nie zgadzam się z moją odpowiedzią, zobacz moją drugą .

Zwykle wzorzec modułu (patrz odpowiedź CMS), który NIE jest wzorzec singletonu, jest wystarczająco dobry. Jednak jedną z cech singletonu jest to, że jego inicjalizacja jest opóźniona do momentu, gdy potrzebny jest obiekt. Wzorzec modułu nie ma tej funkcji.

Moja propozycja (CoffeeScript):

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

Które skompilowane do tego w JavaScript:

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

Następnie mogę wykonać następujące czynności:

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
źródło
Po co ładować moduł, jeśli nie jest potrzebny? A kiedy trzeba załadować moduł, ładuje się moduł i inicjuje się.
Esailija,
6

Krótka odpowiedź:

Ponieważ nieblokujący charakter JavaScript, Singletons w JavaScript są naprawdę brzydkie w użyciu. Zmienne globalne dają również jedną instancję w całej aplikacji bez tych wszystkich wywołań zwrotnych, wzór modułu delikatnie ukrywa elementy wewnętrzne za interfejsem. Zobacz odpowiedź @CMS.

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

};

Stosowanie:

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

Wyjaśnienie:

Singletony dają więcej niż jedną instancję w całej aplikacji: ich inicjalizacja jest opóźniona do pierwszego użycia. To naprawdę wielka sprawa, gdy masz do czynienia z obiektami, których inicjalizacja jest droga. Drogie zwykle oznacza We / Wy, aw JavaScript We / Wy zawsze oznacza oddzwanianie.

Nie ufaj odpowiedziom, które dają ci interfejs instance = singleton.getInstance(), wszystkie pomijają sens.

Jeśli nie przyjmą wywołania zwrotnego do uruchomienia, gdy instancja jest gotowa, nie będą działać, gdy inicjator wykonuje operacje we / wy.

Tak, wywołania zwrotne zawsze wyglądają brzydiej niż wywołanie funkcji, które natychmiast zwraca instancję obiektu. Ale znowu: kiedy wykonujesz operacje we / wy, połączenia zwrotne są obowiązkowe. Jeśli nie chcesz wykonywać żadnych operacji we / wy, tworzenie instancji jest wystarczająco tanie, aby zrobić to na początku programu.

Przykład 1, tani inicjator:

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 setTimeoutpodróbka kosztownej operacji we / wy. To pokazuje, dlaczego singletony w JavaScript naprawdę potrzebują wywołań zwrotnych.

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
źródło
Dzięki próbom i udrękom z innymi odpowiedziami singletonów, które go nie zmniejszyły, wylądowałem na wynikowym kodzie niezwykle podobnym do tego.
mheyman
Z tego czy innego powodu jest to jedyna sensowna dla mnie odpowiedź. Wszystkie pozostałe odpowiedzi przypominają mi odcinek programu goon, w którym trzech mężczyzn próbuje wspiąć się na ścianę na wysokości czterech osób, wspinając się na siebie rekurencyjnie.
Tim Ogilvy,
Układanie w systemie calback jest tym, czego naprawdę potrzebowałem! Dzięki!!
Tim Ogilvy,
Takie podejście nigdy nie daje ci singletona, ponieważ: singleton (singletonInitializer)! == singleton (singletonInitializer) są to dwa różne przypadki. Zwróconą funkcję wynikową można wykorzystać do dołączenia większej liczby wywołań zwrotnych do instancji, ale nie określa ona ściśle, że można utworzyć tylko jedną instancję tego typu. To jest sedno singletonu.
Owen,
6

Ten przykład wziąłem z wzorców JavaScript, które budują lepsze aplikacje z kodowaniem i wzorami projektowymi. Książka Stoyana Stefanova na wypadek, gdybyś potrzebował prostej klasy implementacji, takiej jak obiekt singltone, możesz użyć natychmiastowej funkcji:

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

Możesz sprawdzić ten przykład, wykonując następujący test:

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

Podejście to przechodzi wszystkie przypadki testowe, podczas gdy prywatna implementacja statyczna zakończy się niepowodzeniem, gdy zostanie użyte rozszerzenie prototypu (można to naprawić, ale nie będzie to proste), a publiczna implementacja statyczna mniej wskazana z powodu wystąpienia jest ujawniana publicznie.

jsFiddly demo.

Khamidulla
źródło
5

Myślę, że znalazłem najczystszy sposób na programowanie w JavaScript, ale potrzebujesz wyobraźni. Ten pomysł zaczerpnąłem z techniki pracy w książce „javascript the good parts”.

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

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

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 taki jak ten:

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ć instancję, dzwoniąc

var obj = ClassSingleton.getInstance();

Myślę, że to najładniejszy sposób, ponieważ cała „klasa” nie jest nawet dostępna.

David
źródło
Ale dzięki tej technice możesz mieć więcej niż jedną instancję. To nie jest poprawne.
nicolascolman
1
Nie sądzę, że nie możesz nawet dostać się do klasy bez przejścia przez getInstance. Czy mógłbyś opracować?
David
David Maes Przepraszamy, ale nie zauważyłem walidacji w drugim przykładzie. Przepraszam.
nicolascolman
4

Zarówno @CMS, jak i @zzzzBov dały wspaniałe odpowiedzi, ale tylko po to, by dodać własną interpretację opartą na tym, że przeszedłem do ciężkiego rozwoju node.js z PHP / Zend Framework, w którym wzorce singletonów były powszechne.

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

  • może zostać utworzona jedna i tylko jedna instancja obiektu funkcji
  • instancja nie jest publicznie dostępna i można uzyskać do niej dostęp wyłącznie metodą publiczną
  • konstruktor nie jest publicznie dostępny i można go utworzyć tylko wtedy, gdy nie ma już dostępnej instancji
  • deklaracja konstruktora musi umożliwiać modyfikację łańcucha prototypów. Umożliwi to konstruktorowi dziedziczenie po innych prototypach i zaoferuje metody „publiczne” dla instancji

Mój kod jest bardzo podobny do kodu @ zzzzBov, z tym wyjątkiem, że dodałem do konstruktora łańcuch prototypów i więcej komentarzy, które powinny pomóc osobom pochodzącym z PHP lub podobnego języka w przetłumaczeniu tradycyjnego OOP na JavaScript o charakterze prototypowym. Może nie jest to „najprostsze”, ale uważam, że jest najbardziej odpowiednie.

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

Zauważ, że technicznie samoczynnie wykonująca się funkcja anonimowa sama w sobie jest Singletonem, co dobrze pokazano w kodzie dostarczonym przez @CMS. Jedynym haczykiem jest to, że nie można modyfikować łańcucha prototypów konstruktora, gdy sam konstruktor jest anonimowy.

Pamiętaj, że w przypadku Javascript pojęcia „public” i „private” nie mają zastosowania, tak jak w PHP lub Javie. Ale osiągnęliśmy ten sam efekt, wykorzystując zasady dostępności zakresu funkcjonalnego Javascript.

talentedmrjones
źródło
Z kodu można utworzyć wiele instancji:var a = Singleton.getInstance('foo'); var b = new a.constructor('bar');
zzzzBov
@zzzzBov: Właśnie dostaję błędy, próbując tego w moim skrzypce: jsfiddle.net/rxMu8
cincodenada
4

Nie jestem pewien, dlaczego nikt tego nie poruszał, ale możesz po prostu:

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

  this.foo = function() {
    // whatever
  }
})()
Derek Chiang
źródło
To wydaje się być dobrym sposobem na pominięcie metody getInstance i uzyskanie prostszego rozwiązania. Należy jednak pamiętać, że singleton zostanie wykonany natychmiast po przeanalizowaniu pliku, co oznacza, że ​​detektory DOM muszą być opakowane w funkcję $ (dokument) .ready
HoffZ
4

Najjaśniejszą odpowiedzią powinna być ta z książki Uczenie się wzorców projektowania JavaScript 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 = "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;
    }
 
  };
 
})();

令狐 葱
źródło
3

Uważam, że jest to najprostszy / najczystszy i najbardziej intuicyjny sposób, choć wymaga 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

Alt Eisen
źródło
Jest to całkowicie błędne i new Singleton()
powodowałoby
2

Co jest z tym nie tak?

function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}
Manav
źródło
Test = Klass; t1 = new Test(); t2 = new Test();- brak możliwości zmiany nazwy klasy lub wybrania innej przestrzeni nazw.
zzzzBov,
2

czy mogę włożyć moje 5 monet. Mam funkcję konstruktora, np.

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

Muszę tylko każdy obiekt utworzony przez ten CF będzie taki sam.

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

test

var x1 = new X();
var x2 = new X();
console.log(x1 === x2)
Olencha
źródło
2

Odkryłem, że jest to najłatwiejszy wzorzec Singleton, ponieważ użycie nowego operatora powoduje, że jest on natychmiast dostępny w ramach funkcji, co eliminuje potrzebę zwracania literału obiektu:

var singleton = new (function () {

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

singleton.printSomething();

znak
źródło
2

Oto prosty przykład wyjaśniający wzór singletonu w 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
źródło
1

Potrzebowałem kilku singletonów z:

  • leniwa inicjalizacja
  • parametry początkowe

i tak oto wpadłem na pomysł:

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;
}
  • Argumenty muszą być tablicą, aby to działało, więc jeśli masz puste zmienne, po prostu przekaż []

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

  • parametry nazwy i konstrukcji są tylko ciągiem, aby okno [] działało, ale przy prostym sprawdzaniu typów możliwe jest także window.name i window.construct.

Fred
źródło
1

A co powiesz na ten sposób, po prostu upewnij się, że klasa nie będzie znowu nowa.

Dzięki temu możesz użyć instanceofop, możesz także użyć łańcucha prototypów, aby odziedziczyć klasę, jest to zwykła klasa, ale nie możesz jej nowego, jeśli chcesz uzyskać instancję, użyjgetInstance

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

Jeśli nie chcesz narażać instanceczłonka, po prostu zamknij go.

wener
źródło
1

Poniżej znajduje się fragment mojego przejścia do implementacji wzorca singletonu. To przyszło mi do głowy podczas wywiadu i czułem, że powinienem to gdzieś uchwycić.

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

to samo można znaleźć na moim GIST stronie

ciekawy
źródło
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
źródło
1

Czy to też nie jest 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();
Nawal
źródło
1

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

class YourClass {

    @Singleton static singleton() {}

}

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

Następnie używasz swojego singletona w ten sposób:

var myInstance = YourClass.singleton();

W tym piśmie dekoratory nie są łatwo dostępne w silnikach JavaScript. Musisz upewnić się, że środowisko wykonawcze JavaScript ma włączone dekoratory lub użyć kompilatorów takich jak Babel i TypeScript.

Zauważ też, że instancja singleton jest tworzona jako „leniwa”, tzn. Jest tworzona tylko przy pierwszym użyciu.

Vad
źródło
1

Wzór modułu: w „bardziej czytelnym stylu”. Możesz łatwo zobaczyć, które metody są publicznymi, a które prywatnymi

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

teraz możesz korzystać z metod publicznych takich jak

module.method2 (); // -> Wywołuję metodę prywatną w ramach alertu dotyczącego metody publicznej („cześć, jestem metodą prywatną”)

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

ncubica
źródło
1

Singel:

Upewnij się, że klasa ma tylko jedną instancję i zapewnij do niej globalny punkt dostępu.

Wzorzec Singleton ogranicza liczbę wystąpień określonego obiektu do tylko jednego. Ta pojedyncza instancja nazywa się singletonem.

  • definiuje metodę getInstance (), która zwraca unikalną instancję.
  • odpowiedzialny za tworzenie obiektu instancji i zarządzanie nim.

Obiekt Singleton jest implementowany jako natychmiastowa anonimowa funkcja. Funkcja jest wykonywana natychmiast, zawijając ją w nawiasy, a następnie dwa dodatkowe nawiasy. Nazywa się to anonimowym, ponieważ nie ma nazwy.

Przykładowy program,

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
źródło
1

Najprostsze / najczystsze dla mnie oznacza również po prostu zrozumienie i brak dzwonków i gwizdków, o czym wiele się mówi w wersji Java dyskusji:

Jaki jest skuteczny sposób na implementację wzorca singleton w Javie?

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

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

I tylko częściowo można to przetłumaczyć na JavaScript. Niektóre różnice w Javascript są następujące:

  • konstruktory nie mogą być prywatne
  • Klasy nie mogą zadeklarować pól

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

Wzór singletonu 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ładowe użycie

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

Przykład wyniku

DefaultField1
DefaultField2
Wolfgang Fahl
źródło
1
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
frasq
źródło
1

Jeśli chcesz korzystać z 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);

Dane wyjściowe dla powyższych będą:

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

Kolejny sposób pisania Singletona 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);

Dane wyjściowe dla powyższych będą:

console.log(a.name,a.age,b.name,b.age)// s 1 s 1
sudhee
źródło