我刚刚在javascript中讨论了单例设计的几个主题。我是100%新设计模式的东西,但正如我所看到的,因为根据定义,Singleton不需要实例化,概念上如果它不被实例化,在我看来它不必被视为从蓝图(类)创建的常规对象。所以我的奇怪是为什么不仅仅考虑一个单独的东西,就像静态可用的东西,包含在某种范围内,应该是全部。
从我看到的主题中,他们中的大多数都通过传统的javascript来制作单身
new function(){}
然后创建一个伪构造函数。
嗯,我只是认为一个对象文字足够了:
var singleton = {
dothis: function(){},
dothat: function(){}
}
正确?或者任何人都有更好的见解?
[更新]:我的观点再次表明,为什么人们只是使用更简单的方法在javascript中制作单身,就像我在第二个片段中所示,如果有绝对的原因请告诉我。我经常害怕这种情况,我简化了很多事情:D
答案 0 :(得分:31)
我同意你的看法,最简单的方法是使用对象文字,但如果你想要私有成员,你可以实现利用closures:
var myInstance = (function() {
var privateVar;
function privateMethod () {
// ...
}
return { // public interface
publicMethod1: function () {
// private members can be accessed here
},
publicMethod2: function () {
// ...
}
};
})();
关于new function(){}
构造,它只是使用匿名函数作为constructor function
,该函数内的上下文将是一个将被返回的新对象。
编辑:为了回应@J5's comment,这很简单,实际上我认为这可以成为使用 Lazy Function Definition 模式:
function singleton() {
var instance = (function() {
var privateVar;
function privateMethod () {
// ...
}
return { // public interface
publicMethod1: function () {
// private members can be accessed here
},
publicMethod2: function () {
// ...
}
};
})();
singleton = function () { // re-define the function for subsequent calls
return instance;
};
return singleton(); // call the new function
}
当第一次调用该函数时,我创建了对象实例,并将singleton
重新分配给一个新函数,该函数在其闭包中具有该对象实例。
在第一次调用结束之前,我执行重新定义的singleton
函数,该函数将返回创建的实例。
调用singleton
函数后,只会返回存储在其闭包中的instance
,因为新函数是将要执行的函数。
您可以通过比较返回的对象来证明:
singleton() == singleton(); // true
只有当两个操作数的对象引用相同时,对象的==
运算符才会返回true
,即使对象相同但它们也会返回false是两个不同的例子:
({}) == ({}); // false
new Object() == new Object(); // false
答案 1 :(得分:3)
我已经将第二个版本(var singleton = {};
)用于从Firefox扩展到网站的所有内容,并且它的效果非常好。一个好主意是不要在大括号内定义事物,而是使用对象的名称在其外部定义,如下所示:
var singleton = {};
singleton.dothis = function(){
};
singleton.someVariable = 5;
答案 2 :(得分:3)
ES5规范允许我们使用Object.create():
var SingletonClass = (function() {
var instance;
function SingletonClass() {
if (instance == null) {
instance = Object.create(SingletonClass.prototype);
}
return instance;
}
return {
getInstance: function() {
return new SingletonClass();
}
};
})();
var x = SingletonClass.getInstance();
var y = SingletonClass.getInstance();
var z = new x.constructor();
这很好,因为我们不必担心构造函数泄漏,我们仍然总是使用相同的实例。
这种结构还具有以下优点:我们的Singleton在需要之前不会自行构建。此外,像我们这样使用闭包可以防止外部代码意外或以其他方式使用我们的“实例”变量。我们可以在同一个地方构建更多的私有变量,我们可以定义我们关心在我们的类原型上公开导出的任何内容。
答案 3 :(得分:1)
我也对此感到疑惑,但只是定义一个带有函数的对象对我来说似乎是合理的。没有意义创建一个没有人应该调用的构造函数,创建一个没有原型的对象,当你可以直接定义对象时。
另一方面,如果你想让你的单例成为某个现有“类”的实例 - 也就是说,你希望它有一些其他对象作为它的原型 - 那么你做需要使用构造函数,以便在调用之前设置其prototype
属性。
答案 4 :(得分:1)
单例模式是通过创建一个类来实现的,该类使用一个方法创建一个新的类实例(如果不存在)。如果实例已存在,则只返回对该对象的引用。 1
(function (global) {
var singleton;
function Singleton () {
// singleton does have a constructor that should only be used once
this.foo = "bar";
delete Singleton; // disappear the constructor if you want
}
global.singleton = function () {
return singleton || (singleton = new Singleton());
};
})(window);
var s = singleton();
console.log(s.foo);
var y = singleton();
y.foo = "foo";
console.log(s.foo);
您不只是将单例声明为对象,因为它实例化它,它不会声明它。它也没有提供一种代码机制,它不知道以前对单例的引用来检索它。单例不是单例返回的对象/类,它是一个结构。这与闭包变量不是闭包的方式类似,提供闭包的函数作用域是闭包。
答案 5 :(得分:0)
后一个代码框显示了我所看到的JS开发人员在Javascript中调用他们的OO设计版本。
Singetons意味着是无法构造的单个对象(我想,在初始定义中除外。你有一个单例的全局实例。
答案 6 :(得分:0)
使用“伪构造函数”的关键是它创建了一个新的变量范围。您可以在函数内声明任何嵌套函数内可用的局部变量,但不能在全局范围内声明。
实际上有两种方法可以做到这一点。您可以在示例中使用new
调用该函数,或者直接调用该函数。编写代码的方式略有不同,但它们本质上是等价的。
你的第二个例子可以这样写:
var singleton = new function () {
var privateVariable = 42; // This can be accessed by dothis and dothat
this.dothis = function () {
return privateVariable;
};
this.dothat = function () {};
}; // Parentheses are allowed, but not necessary unless you are passing parameters
或
var singleton = (function () {
var privateVariable = 42; // This can be accessed by dothis and dothat
return {
dothis: function () {
return privateVariable;
},
dothat: function () {}
};
})(); // Parentheses are required here since we are calling the function
您也可以将参数传递给任一函数(您需要在第一个示例中添加括号)。
答案 7 :(得分:0)
Crockford(似乎)同意对象文字是JavaScript中单身人士所需要的全部内容:
答案 8 :(得分:0)
这个怎么样:
function Singleton() {
// ---------------
// Singleton part.
// ---------------
var _className = null;
var _globalScope = null;
if ( !(this instanceof arguments.callee) ) {
throw new Error("Constructor called as a function.");
}
if ( !(_className = arguments.callee.name) ) {
throw new Error("Unable to determine class name.")
}
_globalScope = (function(){return this;}).call(null);
if ( !_globalScope.singletons ) {
_globalScope.singletons = [];
}
if ( _globalScope.singletons[_className] ) {
return _globalScope.singletons[_className];
} else {
_globalScope.singletons[_className] = this;
}
// ------------
// Normal part.
// ------------
var _x = null;
this.setx = function(val) {
_x = val;
}; // setx()
this.getx = function() {
return _x;
}; // getx()
function _init() {
_x = 0; // Whatever initialisation here.
} // _init()
_init();
} // Singleton()
var p = new Singleton;
var q = new Singleton;
p.setx(15);
q.getx(); // returns 15
答案 9 :(得分:0)
我从CMS / CMS' answer偷了这个,并对其进行了更改,以便可以调用它:
MySingleton.getInstance().publicMethod1();
略有改动:
var MySingleton = { // These two lines
getInstance: function() { // These two lines
var instance = (function() {
var privateVar;
function privateMethod () {
// ...
console.log( "b" );
}
return { // public interface
publicMethod1: function () {
// private members can be accessed here
console.log( "a" );
},
publicMethod2: function () {
// ...
privateMethod();
}
};
})();
singleton = function () { // re-define the function for subsequent calls
return instance;
};
return singleton(); // call the new function
}
}