tips:继承必备函数
function inheritPrototype(subType, superType){
var prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
应该确保单体对象只存在一份实例。这种模式在JS中十分重要,因为使用单体对象创建的命名空间是清除全局变量的手段之一(我们知道在网页环境中使用全局变量风险很大)。
单体的基本结构
var Singleton{
attr1:10,
attr2:20,
...
method1:function(){},
...
}
这就是一个最简单的单体模式,但是,这个对象可以被任意修改,这实际上违背了面向对象的设计准则之一:开放封闭原则。
划分命名空间
由于JS中,变量没有类型的限制,而且可以任意被改写,所以,如果我们就把变量放在全局变量中,那么它就很容易被改写。
var someImportantFunction = function(){...},
...
someImportantFunction = 100;//变量被改写
...
更佳实践:
var myNameSpace = {
someImportantFunction : function(){...},
...
}
someImportantFunction = 100;//不会对myNameSpace.someImportantFunction有任何影响
这样,用命名空间的方式把相关的变量和方法组织在一起,有利于增强代码的文档性。
拥有私有成员的单体
方法1:下划线表示法表示私有成员,也是告诫其他程序员不要访问特定成员的简明方法。
var MySingleton = {
_privateMethod = function(){...},
_anotherPrivateMethod = function(){...},
...
publicFunction = function(){
this._privateMethod();
},
...
}
方法2:使用闭包。
这种方式又称为模块模式。
MyNameSpace.singleton = (function(){
// private attrs and methods
var privateAttr1 = 1;
...
function privateMethod = function(){};
...
return {
//public attrs and methods
attr1:1,
attr1:2,
...
method1:function(){},
...
}
})();
惰性实例化
前面这几种方式定义的单体对象都有一个共同的特点:它们在脚本运行的时候就被加载出来了。但其实有时候,我们并不需要马上加载这些对象,仅在用户需要的时候加载即可。这就用到了接下来我们要说的惰性实例化。
我们可以这样修改上面的闭包单体代码来实现惰性实例化:
MyNameSpace.singleton = (function(){
function constructor(){
// 这个方法中的所有代码就是最原始的闭包方式定义单体的代码。
// constructor是一个私有方法,这也正是我们需要的--别人不能随意访问这个方法。
// private attrs and methods
var privateAttr1 = 1;
...
function privateMethod = function(){};
...
return {
//public attrs and methods
attr1:1,
attr1:2,
...
method1:function(){},
...
}
}
// 另一个私有变量,存放着唯一单体对象。
var uniqueInstance;
return {
getInstance : function(){
if(!uniqueInstance){
uniqueInstance = constructor();
}
return uniqueInstance;
}
}
})();
此时,我们对单体方法的调用应该是:
MyNameSpace.singleton.getInstance.somePublicFunction();
分支
分支技术是用来把浏览器差异封装到运行期间设置的动态方法中的技术。
举个例子来说,比如我们想要利用AJAX获取数据,那么我们就需要获取XHR对象。在大多数浏览器中,我们可以通过XMLHttpRequest的实例来获取,但是在早期的IE中,我们需要的却是ActiveX对象。那么我们就需要某种浏览器探测技术来帮助我们去针对不同的浏览器获取响应的对象。如果我们不采用分支技术,那么我们每次需要XHR的时候,都要进行一次浏览器的判别。在其他情境下,也许我们的判别方式很复杂或者调用很频繁,难免会使得代码效率降低。
有效的方法就是,只在脚本加载的时候一次性的判定好针对脚本运行的浏览器该用哪些方法,也就是说代码是在运行时动态确定的。
MyNameSpace.singleton = (function(){
var objectA = {
methodA : function(){},
methodB : function(){},
...
};
var objectB = {
methodA : function(){},
methodB : function(){},
...
};
return condition ? objectA : objectB;
})();
但是,这种模式存在弊端:objectA和objectB我们只用了一个,另一个则没有用到白白占用了内存。因此在考虑使用这项技术的时候,我们要根据需求,权衡内存和运行效率之后再作出选择。