In Javascript ist das Singleton-Muster eines der grundlegendsten und am häufigsten verwendeten Entwurfsmuster. Es kann sein, dass Sie das Singleton-Muster versehentlich verwenden.
Dieser Artikel beginnt mit der grundlegendsten Theorie, beschreibt die Grundkonzepte und die Implementierung des Singleton-Musters und beschreibt schließlich anhand eines Beispiels die Anwendung des Singleton-Musters.
Theoretische Basis
Konzept
Der Singleton-Modus bedeutet, wie der Name schon sagt, dass nur eine Instanz existiert. Der Singleton-Modus kann sicherstellen, dass es nur eine Instanz einer Klasse im System gibt und dass die Instanz von außen leicht zugänglich ist, wodurch die Kontrolle über die Anzahl der Instanzen erleichtert und Systemressourcen gespart werden. Wenn nur ein Objekt einer bestimmten Klasse im System vorhanden sein soll, ist das Singleton-Muster die beste Lösung.
Grundstruktur
Das einfachste Singleton-Muster beginnt mit einem Objektliteral, das zusammengehörige Eigenschaften und Methoden organisiert.
var singleton = { prop:"value", method:function(){ } }
In dieser Form des Singleton-Modus sind alle Mitglieder öffentlich und können über Singleton aufgerufen werden. Der Nachteil dabei ist, dass es im Singleton einige Hilfsmethoden gibt, von denen nicht erwartet wird, dass sie den Benutzern zugänglich gemacht werden. Wenn der Benutzer diese Methoden verwendet und dann bei der nachfolgenden Wartung einige Hilfsmethoden gelöscht werden, führt dies zu Programmfehlern.
Wie vermeidet man solche Fehler?
Singleton-Muster mit privaten Mitgliedern
Wie erstelle ich private Mitglieder in einer Klasse? Dies wird durch die Anforderung von Abschlüssen erreicht. In diesem Artikel wird nicht näher darauf eingegangen.
Die Grundform lautet wie folgt:
var singleton = (function () { var privateVar = "private"; return { prop: "value", method: function () { console.log(privateVar); } } })();
Die erste ist eine selbstausführende anonyme Funktion. In der anonymen Funktion wird eine Variable privateVar deklariert und ein Objekt zurückgegeben und dem Singleton-Objekt zugewiesen. Auf die Variable privateVar kann nicht außerhalb der anonymen Funktion zugegriffen werden. Sie ist die private Variable des Singleton-Objekts. Auf diese private Variable kann nur innerhalb der Funktion oder über exponierte Methoden zugegriffen werden. Diese Form wird auch Modulmuster genannt.
Verzögerte Instanziierung
Unabhängig davon, ob es sich um ein direktes Literal oder einen privaten Member-Singleton-Modus handelt, handelt es sich bei beiden um Singletons, die beim Laden des Skripts erstellt werden. Manchmal verwendet die Seite dieses Singleton-Objekt jedoch möglicherweise nie, was zu einer Verschwendung von Ressourcen führt. Der beste Weg, mit dieser Situation umzugehen, ist Lazy Loading, was bedeutet, dass das Singleton-Objekt erst dann tatsächlich instanziiert wird, wenn es benötigt wird.
var singleton = (function () { function init() { var privateVar = "private"; return { prop: "value", method: function () { console.log(privateVar); } } } var instance = null; return { getInstance: function () { if (!instance) { instance = init(); } return instance; } } })();
Kapseln Sie zunächst den Code zum Erstellen eines Singleton-Objekts in der Init-Funktion, deklarieren Sie dann eine private Variableninstanz, um die Instanz des Singleton-Objekts darzustellen, und stellen Sie eine Methode getInstance bereit, um das Singleton-Objekt abzurufen.
Beim Aufruf wird es über singleton.getInstance() aufgerufen. Das Singleton-Objekt wird tatsächlich erstellt, wenn getInstance aufgerufen wird.
Anwendbare Anlässe
Das Singleton-Muster ist das am häufigsten verwendete Entwurfsmuster in JS. Unter den Gesichtspunkten der Verbesserung der Modularität und der Code-Organisation sollte das Singleton-Muster so oft wie möglich verwendet werden. Es kann verwandte Codes für eine einfache Wartung zusammenfassen. Bei großen Projekten kann das verzögerte Laden jedes Moduls die Leistung verbessern, Implementierungsdetails verbergen und häufig verwendete APIs verfügbar machen. Gängige Klassenbibliotheken wie Underscore und jQuery können als Singleton-Modus-Anwendungen verstanden werden.
Kombiniert mit tatsächlichem Kampf
Wie bereits erwähnt, ist das Singleton-Muster eines der am häufigsten verwendeten Entwurfsmuster. Lassen Sie uns zur Veranschaulichung ein Beispiel geben:
Der folgende Code implementiert hauptsächlich eine einfache Datumshilfsklasse, die im Singleton-Modus implementiert wird:
Grundlegende Singleton-Musterstruktur
var dateTimeHelper = { now: function () { return new Date(); }, format: function (date) { return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(); } }; console.log(dateTimeHelper.now());<br />
Dieser Code implementiert den Singleton-Modus über Objektliterale, und Sie können die Methode direkt aufrufen, wenn Sie sie verwenden.
Lazy Loading implementiert Singleton-Muster
var dateTimeHelper = (function () { function init() { return { now: function () { return new Date(); }, format: function (date) { return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(); } } } var instance = null; return { getInstance: function () { if (!instance) { instance = init(); } return instance; } } })(); console.log(dateTimeHelper.getInstance().now())
Dies ist das Lazy-Loading-Singleton-Muster.
Schauen wir uns noch ein paar Beispiele an:
Implementierung 1: Das einfachste Objektliteral
var singleton = { attr : 1, method : function(){ return this.attr; } } var t1 = singleton ; var t2 = singleton ;
Also offensichtlich t1 === t2.
Es ist sehr einfach und sehr benutzerfreundlich. Der Nachteil besteht darin, dass keine Kapselung erfolgt und alle Attributmethoden verfügbar sind. Für einige Situationen, in denen private Variablen verwendet werden müssen, scheint dies unzureichend zu sein. Natürlich gibt es dabei auch gewisse Nachteile.
Implementierung 2: Interne Beurteilung des Konstruktors
Tatsächlich ähnelt es in gewisser Weise der ursprünglichen JS-Implementierung, mit der Ausnahme, dass die Beurteilung, ob eine Instanz der Klasse bereits vorhanden ist, im Konstruktor platziert wird.
function Construct(){ // 确保只有单例 if( Construct.unique !== undefined ){ return Construct.unique; } // 其他代码 this.name = "NYF"; this.age="24"; Construct.unique = this; } var t1 = new Construct() ; var t2 = new Construct() ;
那么也有的, t1 === t2 。
也是非常简单,无非就是提出一个属性来做判断,但是该方式也没有安全性,一旦我在外部修改了Construct的unique属性,那么单例模式也就被破坏了。
实现3 : 闭包方式
对于大着 灵活 牌子的JS来说,任何问题都能找到 n 种答案,只不过让我自己去掂量孰优孰劣而已,下面就简单的举几个使用闭包实现单例模式的方法,无非也就是将创建了的单例缓存而已。
var single = (function(){ var unique; function Construct(){ // ... 生成单例的构造函数的代码 } unique = new Constuct(); return unique; })();
只要 每次讲 var t1 = single; var t2 = single;即可。 与对象字面量方式类似。不过相对而言更安全一点,当然也不是绝对安全。
如果希望会用调用 single() 方式来使用,那么也只需要将内部的 return 改为
return function(){ return unique; }
以上方式也可以使用 new 的方式来进行(形式主义的赶脚)。当然这边只是给了闭包的一种例子而已,也可以在 Construct 中判断单例是否存在 等等。 各种方式在各个不同情况做好选着即可。
总结
单例模式的好处在于对代码的组织作用,将相关的属性和方法封装在一个不会被多次实例化的对象中,让代码的维护和调试更加轻松。隐藏了实现细节,可以防止被错误修改,还防止了全局命名空间的污染。另外可以通过惰性加载提高性能,减少不必要的内存消耗。