这篇文章主要介绍了什么是单例单例模式、使用场景,提供了3个示例给大家参考
什么是单例?
单例要求一个类有且只有一个实例,提供一个全局的访问点。因此它要绕过常规的控制器,使其只能有一个实例,供使用者使用,而使用着不关心有几个实例,因此这是设计者的责任
In JavaScript, Singletons serve as a shared resource namespace which isolate implementation code from the global namespace so as to provide a single point of access for functions.
在javascript中,单例被当做一个全局的命名空间,提供一个访问该对象的一个点。
使用场景
In practice, the Singleton pattern is useful when exactly one object is needed to coordinate others across a system.
单例比较适用于一个对象和其他系统进行交互。
类比
单例有点类似于一个小组的小组长,在一段时间内只有一个小组长,有小组长来指定组员的工作,分配和协调和组员的工作。
实例1:这个是最简单的单例,通过key,value的形式存储属性和方法
var A = { xx:3, yy:4, B:function(el){ }, C:function(el){ }, D:function(el){ }, E:function(el){ } }
实例2:首先创建一个实例的引用,然后判断这个实例是否存在,如果不存在那么就创建,存在的话,就直接返回,保证有且只有一个。
var mySingleton = (function () { // Instance 存储一个单例实例的引用 var instance; function init() { // Singleton // 私有的方法和变量 function privateMethod(){ console.log( "I am private" ); } var privateVariable = "Im also private"; return { // 共有的方法和变量 publicMethod: function () { console.log( "The public can see me!" ); }, publicProperty: "I am also public" }; }; return { // 如果实例不存在,那么创建一个 getInstance: function () { if ( !instance ) { instance = init(); } return instance; } }; })(); var singleA = mySingleton; var singleB = mySingleton; console.log( singleA === singleB ); // true
实例3:
var SingletonTester = (function () { // options: an object containing configuration options for the singleton // e.g var options = { name: "test", pointX: 5}; function Singleton( options ) { // set options to the options supplied // or an empty object if none are provided options = options || {}; // set some properties for our singleton this.name = "SingletonTester"; this.pointX = options.pointX || 6; this.pointY = options.pointY || ; } // our instance holder var instance; // an emulation of static variables and methods var _static = { name: "SingletonTester", // Method for getting an instance. It returns // a singleton instance of a singleton object getInstance: function( options ) { if( instance === undefined ) { instance = new Singleton( options ); } return instance; } }; return _static; })(); var singletonTest = SingletonTester.getInstance({ pointX: 5 }); // Log the output of pointX just to verify it is correct // Outputs: 5 console.log( singletonTest.pointX );
单例模式
单例模式也称作为单子模式,更多的也叫做单体模式。为软件设计中较为简单但是最为常用的一种设计模式。
下面是维基百科对单例模式的介绍:
在应用单例模式时,生成单例的类必须保证只有一个实例的存在,很多时候整个系统只需要拥有一个全局对象,才有利于协调系统整体的行为。比如在整个系统的配置文件中,配置数据有一个单例对象进行统一读取和修改,其他对象需要配置数据的时候也统一通过该单例对象来获取配置数据,这样就可以简化复杂环境下的配置管理。
单例模式的思路是:一个类能返回一个对象的引用(并且永远是同一个)和一个获得该实例的方法(静态方法,通常使用 getInstance 名称)。那么当我们调用这个方法时,如果类持有的引用不为空就返回该引用,否者就创建该类的实例,并且将实例引用赋值给该类保持的那个引用再返回。同时将该类的构造函数定义为私有方法,避免其他函数使用该构造函数来实例化对象,只通过该类的静态方法来得到该类的唯一实例。
对于 JS 来说,巨大的灵活性使得其可以有多种方式实现单例模式,使用闭包方式来模拟私有数据,按照其思路可得:
- var single = (function(){
- var unique;
- function getInstance(){
- if( unique === undefined ){
- unique = new Construct();
- }
- return unique;
- }
- function Construct(){
- // ... 生成单例的构造函数的代码
- }
- return {
- getInstance : getInstance
- }
- })();
以上,unique便是返回对象的引用,而 getInstance便是静态方法获得实例。Construct 便是创建实例的构造函数。
可以通过 single.getInstance() 来获取到单例,并且每次调用均获取到同一个单例。这就是 单例模式 所实现的效果。
不过,对于JS来说,显然以上循规蹈矩的方式显得过于笨重,在不同的场景以不同的方式实现单体模式正是 JS 的优势
实现1: 最简单的对象字面量
- var singleton = {
- attr : 1,
- method : function(){ return this.attr; }
- }
- var t1 = singleton ;
- var t2 = singleton ;
那么很显然的, t1 === t2 。
十分简单,并且非常使用,不足之处在于没有什么封装性,所有的属性方法都是暴露的。对于一些需要使用私有变量的情况就显得心有余而力不足了。当然在对于 this 的问题上也是有一定弊端的。
实现2:构造函数内部判断
其实和最初的JS实现有点类似,不过是将对是否已经存在该类的实例的判断放入构造函数内部。
- 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 中判断单例是否存在 等等。 各种方式在各个不同情况做好选着即可。
总结
总的来说,单例模式相对而言是各大模式中较为简单的,但是单例模式也是较为常用并且很有用的模式。在JS中尤为突出(每个对象字面量都可以看做是一个单例么~)。
记住,是否严格的只需要一个实例对象的类(虽然JS没有类的概念),那么就要考虑使用单例模式。
使用数据缓存来存储该单例,用作判断单例是否已经生成,是单例模式主要的实现思路。