用JavaScript实现单例模式的最简单/最干净的方法是什么?
当前回答
在ES6中正确的方法是:
MyClass { 构造函数(){ if (MyClass._instance) { 抛出新的错误("单例类不能实例化超过一次。") } MyClass。_instance = this; / /……其余的构造函数代码在此之后 } } var instanceOne = new MyClass() //执行成功 var instanceTwo = new MyClass() //抛出错误
或者,如果你不想在创建第二个实例时抛出错误,你可以只返回最后一个实例,如下所示:
MyClass { 构造函数(){ if (MyClass._instance) { 返回MyClass._instance } MyClass。_instance = this; / /……其余的构造函数代码在此之后 } } var instanceOne = new MyClass() var instanceTwo = new MyClass() console.log(instanceOne === instanceTwo) //日志为true
其他回答
简单的例子
class Settings {
constructor() {
if (Settings.instance instanceof Settings) {
return Settings.instance;
}
this.settings = {
id: Math.floor(Math.random() * 4000),
name: "background",
};
Object.freeze(this.settings);
Object.freeze(this);
Settings.instance = this;
}
}
var o1 = new Settings();
var o2 = new Settings();
console.dir(o1);
console.dir(o2);
if (o1 === o2) {
console.log("Matched");
}
你可以用下面这个例子中的TypeScript装饰器来做:
class YourClass {
@Singleton static singleton() {}
}
function Singleton(target, name, descriptor) {
var instance;
descriptor.value = () => {
if(!instance) instance = new target;
return instance;
};
}
然后你像这样使用你的单例:
var myInstance = YourClass.singleton();
在撰写本文时,JavaScript引擎中还没有装饰器。你需要确保你的JavaScript运行时实际上启用了装饰器,或者使用像Babel和TypeScript这样的编译器。
还要注意的是,单例实例是“惰性”创建的,也就是说,只有当你第一次使用它时才会创建它。
我从JavaScript模式中得到了这个例子 用编码和设计模式构建更好的应用程序一书(Stoyan Stefanov著)。如果你需要一些简单的实现类,比如单例对象,你可以使用一个直接函数,如下所示:
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
};
};
}());
你可以通过下面的测试用例来检查这个例子:
// 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
这种方法通过了所有测试用例,而当使用原型扩展时,私有静态实现将失败(它可以被修复,但并不简单),而公共静态实现则不太可取,因为实例是公开给公众的。
jsFiddly demo.
您可以在每次新的执行中返回相同的实例-
function Singleton() {
// lazy
if (Singleton.prototype.myInstance == undefined) {
Singleton.prototype.myInstance = { description: "I am the instance"};
}
return Singleton.prototype.myInstance;
}
a = new Singleton();
b = new Singleton();
console.log(a); // { description: "I am the instance"};
console.log(b); // { description: "I am the instance"};
console.log(a==b); // true
我喜欢将单例模式和模块模式结合起来使用,并将初始化时分支与Global NS检查结合起来使用,并将它们包装在一个闭包中。
在初始化单例后环境不会改变的情况下,使用立即调用的object-literal来返回一个充满实用程序的模块,该模块将持续一段时间。
我没有传递任何依赖项,只是在它们自己的小世界中调用单例对象——唯一的目标是:为事件绑定/解绑定创建一个实用程序模块(设备方向/方向变化也可以在这种情况下工作)。
window.onload = ( function( _w ) {
console.log.apply( console, ['it', 'is', 'on'] );
( {
globalNS : function() {
var nameSpaces = ["utils", "eventUtils"],
nsLength = nameSpaces.length,
possibleNS = null;
outerLoop:
for ( var i = 0; i < nsLength; i++ ) {
if ( !window[nameSpaces[i]] ) {
window[nameSpaces[i]] = this.utils;
break outerLoop;
};
};
},
utils : {
addListener : null,
removeListener : null
},
listenerTypes : {
addEvent : function( el, type, fn ) {
el.addEventListener( type, fn, false );
},
removeEvent : function( el, type, fn ) {
el.removeEventListener( type, fn, false );
},
attachEvent : function( el, type, fn ) {
el.attachEvent( 'on'+type, fn );
},
detatchEvent : function( el, type, fn ) {
el.detachEvent( 'on'+type, fn );
}
},
buildUtils : function() {
if ( typeof window.addEventListener === 'function' ) {
this.utils.addListener = this.listenerTypes.addEvent;
this.utils.removeListener = this.listenerTypes.removeEvent;
} else {
this.utils.attachEvent = this.listenerTypes.attachEvent;
this.utils.removeListener = this.listenerTypes.detatchEvent;
};
this.globalNS();
},
init : function() {
this.buildUtils();
}
} ).init();
} ( window ) );
推荐文章
- 使用jQuery以像素为整数填充或边距值
- 检查是否选择了jQuery选项,如果没有选择默认值
- Next.js React应用中没有定义Window
- 如何重置笑话模拟函数调用计数之前,每次测试
- 如何强制一个功能React组件渲染?
- 在javascript中从平面数组构建树数组
- 将Dropzone.js与其他字段集成到现有的HTML表单中
- 如何在AngularJS中观察路由变化?
- JavaScript DOM删除元素
- 将dd-mm-yyyy字符串转换为日期
- Javascript复选框onChange
- Javascript函数前导bang !语法
- 如何在页面上遍历所有DOM元素?
- 在JS/jQuery中触发按键/按键/按键事件?
- 如何每5秒重新加载页面?