单体模式的概述:
单体是一个只能被实例化一次并且可以通过一个众所周知的访问点访问的类。
单体是一个用来划分命名空间并将一批相关方法和属性组织在一起的对象。如果它可以被实例化,那么他只能被实例化一次。
单体对象的区分;
1.并非所有的对象字面量都是单体。如果他只是用来模仿关联数组或容纳数据的话,那就不是单体。
2.如果用来组织一批相关方法和属性话。那就可能是单体。
1.单体的基本结构
最简单的单体实际上就是一个对象字面量,它把一批有一定关联的方法和属性组织在一起
var Singleton = {
attribute1 : true,
attribute2 : 10,
method1:function(){
},
method2:function(){
}
}
类分析:
1.这个单体对象被修改。可以为对象添加新成员。
2.可以使用delete运算符删除其现有成员。
3.面向对象设计的原则:类可以被扩展,但是不能 被修改。
5.2划分命名空间
单体对象有两部分组成:
包含着方法和属性成员的对象自身。以及用来访问它的变量。(这个变量通常是全局性的,以便在网页上任何地方都能直接访问到它所指向的单体对象。) 这是单体模式的一个要点。
命名空间是可靠的javascript编程的一个重要工具。
为了避免无意中改下变量,最好的解决方法之一是用单体对象将代码组织在命名空间之中。
var Mynamespace = {
findProduct : function(id){
}
}
分析:
1.现在findProduct函数是一个Mynamespace的中一个方法,不会被全局命名空间中声明的任何新变量改写。
2.用命名空间把类似的方法组织在一起。有助于增强代码的文档性。
命名空间进一步分割。
各种javascript代码,都有可能出现在全局命名空间中。
为了避免冲突,可以定义一个用来包含自己的所有代码的全局对象:
var GiantCorp = {};
2.然后可以分闷别类地把自己的代码和数据组织到这个全局对象中的各种对象中:
GiantCorp.Common = {
}
5.3 用作特定网页专用代码的包装器的单体
场景描述:
在有很多网页网页的网站中,有些javascript代码是所有的网页都要用到的,所以他们被存放在独立的文件中,有些代码是某个网页专用的。不会被用到其他地方。
建议:最后把这两种代码分别包装在自己的单体对象中。
包装特定网页的专用代码的单体的骨架:
Namespace.PageName = {
CONSTANT_1:true,
CONSTANT_2:10,
method1:function(){
},
method2:function(){
}
init:function(){
}
}
5.4拥有私有成员的单体
5.4.1使用下划线表示法
1.在单体对象内创建私有成员最简单,。最直接了当的办法是使用下划线表示法。
2.在单体对象中使用下划线表示法是一种告诫其他程序员不要直接访问特定成员的简明方法
GiantCorp.DataParser = {
_stripWhitespace :function(str){
return str.replace(/\s+/,‘‘);
},
_stringSplit:function(str,delimiter){
return str.split(delimiter)
},
string:function(str,delimiter,stripws){
if(stripws);
str = this.__stripWhitespace(str);
var outputArray = this._stringSplit(str,delimiter);
return outputArray;
}
}
5.4.2 使用闭包
在单体对象中创建私有成员的第二种方法需要借助闭包,这个和第3章中创建真正的私有成员的做法非常相似。但是有一个(重要的区别)
1.之前的做法是吧变量和函数定义在构造函数体内,让他变成私有成员。
2.构造函数体内定义了所有的特权方法并用this关键字使其可被外界访问。
3.每次生成一个该类的实例时,所有的声明的构造函数内的方法和属性都会再次创建一次。
单体模式使用闭包:
1.单体只会被实例化一次。不用担心自己在构造函数中声明了多少成员。
2.每个属性和方法都会被创建一次。所有可以把他们声明在构造函数内部
mynamespace.singleton = {};
创建一个定义之后立即执行的函数创建单体:
mynamespace.singleton = function{
return{};
}();
类的作用和分析:
1.第二个例子中并没有把一个函数复制给mynamespace.singleton。
2.匿名函数返回类一个对象,而赋给mynamespace.singletion变量的正是这个对象。
3.为了理解执行这个匿名函数,只需在其定义的最后那个大括号后面放上一堆圆括号即可。
5.4.3两种技术的比较
看dataparser例子,看看如何在其实现中使用真正的私有成员,不在是通过下划线为每个私有方法。
giantcorp.dataparser = (function(){
//私有属性
var while = /\s+/;
//私有方法
function stripwhitespace(str){
return str.replace(while,‘‘);
}
function stringsplit(str,delimiter){
return str.split(delimiter);
}
return {
stringtoarray:function(str,delimiter,stripws){
if(stripws){
str = stripwhitespace(str);
}
}
var outputArray = stringSplit(str,delimiter);
retrun outputArray;
}
})();
类分析:
1.现在这些私有方法和属性可以直接用其名称访问,不必在其前面加上“this.”或“dataparser.” 这些前缀只用于访问单体对象的公有成员。
这个模式与使用下划线表示法的模式比较的几点优势
1.把私有成员放到闭包中可以确保其不会在单体对象之外被使用。
2.可以自由的改变对象的实现细节。不会殃及别人的代码。
3.这种方法对数据进行保护和封装。
单体模式带来的好处:
1.在使用这种模式时,你可以享受到真正的私有成员带来的所有好处。
2.单体类只会被实例化一次。
3.单体模式是javascript种最流行、应有最广泛的模式之一。
5.5惰性实例化:
前面所讲的单体模式的各种方法实现方式有一个共同点:
1.单体对象都是在脚本加载时被创建出来
2.对于资源密集型的或配置开销甚大的单体,在需要用到的才创建对象。这个技术被称为懒惰加载。
3.作为命名空间。特定网页专用的代码包装器或者组织相关实用的方法还是立即加载。
4.懒惰加载单体的特别之处。访问他们必须借助于一个静态。
调用方式:Singleton.geninstance().methodname()而不是这样调用:singleton.methodname().
5.getinstace方法会检查该单体是否已经被实例化。如果没有。那么它将创建并返回其实例。已经实例化就会返回现在的实例。
6. 把普通的单体转化为惰性加载单体:
mynamespace.singleton = (function(){
var privateAttribute1 = false;
var privateAttribute2 = [1,2,3];
function privateMethod1(){
}
function privateMethod2(args){
}
return {
publicAttribute1 : true,
publicAttribute2 : 10,
publicMethod1 : function(){
},
publicMethod2 : function(){
}
}
})()
转化第一步:把单体的所有代码移到一个名为constructor方法中:
mynamespace.singleton = (function(){
function constructor(){
var privateAttribute1 = false;
var privateAttribute2 = [1,2,3];
function privateMethod1(){
}
function privateMethod2(args){
}
return {
publicAttribute1 : true,
publicAttribute2 : 10,
publicMethod1 : function(){
},
publicMethod2 : function(){
}
}
}
})()
类分析:
1. 这个方法不能从闭包外部访问,所以我们可以全权控制器调用时机。
2.公有方法getInstances是用来控制实现这种控制。
3.getInstace要变成公有方法,需要放到一个对象字面量返回对象即可;
examples:
mynamaespace.singleton = (function(){
function constructor (){
}
return {
getinstace:{
}
}
})()
编写用于控制单体类的实例化时机的代码,需要中两件事:
1.必须知道该类是否被实例化过。
2.如果该类已经被实例化过。那么他需要掌握其实力的情况,以便能返回这个实例
3.办这两件事需要用一个私有属性的已有的私有方法constructor
mynamespace.singleton = (function(){
var uniqueInstace;
function constructor(){}
return {
getInstace : function(){
if(uniqueinstace){
uniqueInstace = constructor();
}
return uniqueInstace;
}
}
})()
调用方式:mynamespace.singleton.getinstace().publicmethod1();
命名空间太长简化分: var mns = mynamespace.singleton;
5.6分支
5.8单体模式的适应场合
1。从未代码的提供命名空间和增强模块的角度来说,应该尽量多使用单体模式。
2.