JavaScript 单体模式详解:命名空间、私有成员与浏览器兼容封装

作者:窗边听雨

时间:2026-10-24

浏览:651

本文深入解析 JavaScript 单体模式(Singleton),阐述其作为最基本且实用的设计模式的核心价值。内容涵盖如何利用单体划分命名空间以消除全局变量污染,通过闭包实现私有成员与公有成员的访问控制,以及利用分支技术封装 XMLHttpRequest 的浏览器差异。文章提供完整代码示例与注意事项,帮助开发者写出更整洁、可维护的 JavaScript 代码。

文章目录
单体模式(singleton)

单体模式基础与结构

单体模式在脚本加载阶段创建,它将一系列相关的变量和方法整合为一个逻辑单元,并通过单一的全局变量来访问该单元内的所有内容。



一个单体主要分为三部分

访问内部信息的入口变量(例如 Sky)。


属性成员(例如 nickName、age、timeInfo)。


方法成员(例如 sayHello)。



基本结构

具体实现代码如下:

 
var Sky = {
/* 
* 作用一,变量管理
*/ 
nickName: "sky",
age: "26",
/* 
* 作用二,加载中初始化变量
* 在加载过程中执行并初始化Sky.info
*/ 
timeInfo: function()
    { 
var _year = new Date().getFullYear();
return _year;
}(), 
/* 
* 作用三,函数管理,让你的函数看起来不再那么散乱
*/ 
sayHello: function()
    { 
alert("hello,world!");
} 
} 
//所有内部信息通过Sky这个变量进行访问;
alert(Sky.timeInfo);


以下是更详细的说明,看完了这篇文章,相信你应该差不多了解了,网上好多高手的js写法了,单体模式很常用。

单体模式的核心特点

单体是一种用于划分命名空间并将一批相关的属性和方法组织在一起的对象。如果该单体可以被实例化,那么它只能被实例化一次。


单体模式是 JavaScript 中最基础但也最有用的设计模式之一。


特点:



. 可以来划分命名空间,从而清除全局变量所带来的危险。


. 利用分支技术来来封装浏览器之间的差异。


. 可以把代码组织的更为一体,便于阅读和维护。



单体的基本结构(正确写法):

代码如下:

 
/*Basic Singleton*/
var Singleton = {
attribute1:true,
attribute2:10,
method1:function(){},
method2:function(){}
}; 

划分命名空间:

利用单体划分命名空间

代码如下:

 
var box = {
width:0,
height:0,
getArea:function(){
return this.width*this.height;//js中对象成的访问必须是显示的,即this是不能省略的
}, 
init:function(w,h){
// width = w;
// height = h;这种方式相当于定义了两个全局变量,(没加var声明的变量为全局变量)
// 并不是对对象width和height的赋值
//下面是正确的
this.width = w;
this.height = h;
} 
};//box划分了一个命名空间,命名空间里的变量只在空间里有效

上面的单体中的所有的成员以及方法都是公有的(public),也就是在单体的外部可以对他们进行任意的改动,那为什么说单体提供了一个命名空间呢?



我们继续:

代码如下:

 
var box = {
width:0,
height:0,//单体的变量
getArea:function(){
return width*height;//中的,width,height其实并不是单体的变量,而是在init中定义的全局变量
} 
init:function(w,h){
width = w;
height = h;
} 
};//init中width,height其实并不是单体的变量
window.onload = function(){
var init = box.getArea();
alert(init);
} 

由于没有对init中的width,height进行初始化,所以会报错,这样改一下:


代码如下:

 
var box = {
width:0,
height:0,
getArea:function(){
return width*height;
}, 
init:function(w,h){
width = w;
height = h;
} 
} 
window.onload = function(){
width = 0;
height = 0;
//or box.init(0,0);
var init = box.getArea();
alert(init);
} 

发现可以了,由于init和 getArea所用的width和height并不是归单体所有的变量,而是一个全局变量,所以我们可以在单体外面进行随意调用而不受影响



如果我们这样写一下就更明白了:



代码如下:

 
var box = {
width:0,
height:0,
getArea:function(){
return width*height;//js中对象成的访问必须是显示的,即this是不能省略的
}, 
init:function(w,h){
width = w;
height = h;
} 
};//这里的width,height其实并不是单体的对象
window.onload = function(){
width = 0;
height = 0;
var width = box.getArea();
alert(width);
} 

这样写又会报错了,可见我们以上的方式对于全局变量并没有建立起一个命名空间,全局变量为我们带来了危险。所以最上面的写法是对的,我们来验证一下:


代码如下:

 
var box = {
width:2,
height:2,
getArea:function(){
return this.width*this.height;//js中对象成的访问必须是显示的,即this是不能省略的
}, 
init:function(w,h){
this.width = w;
this.height = h;
} 
} 
window.onload = function(){
width = 0;
height = 0;
var width = box.getArea();
alert(width);
} 

可见在window.onload中的width 和height已经没有干扰了,因为单体为单体中的width和height建立了一个命名空间。



成员的属性:

私有成员与公有成员的封装

讨论完命名空间,我们来对单体变量和方法的属性做一下设定。学过其他语言的人(java,c++,c#...)都应该很了解其中类成员的public和private,


虽然在javascript中没有这么严格的面向对象(oop),但是我们可以借助闭包来进行一个模仿,毕竟有的变量设为public是很不好的。



代码如下:

 
var circle = (function(){
//pravite member!
var r = 5;
var pi = 3.1416;//后面用分号
return{//public member
getArea:function(){
return r*r*pi;//访问私有成员不要加this
},//后面用逗号
//如果想改变r和pi的值,只能通过设置一个公有的函数来实现
init:function(setR){
r = setR;
} 
} 
})() 
window.onload = function(){
circle.r = 0;//无法访问私有成员,相当于又为circle创建了一个共有成员r
alert(circle.getArea());
circle.init(0);//通过公有的工具函数便可以访问了。
alert(circle.getArea());
}; 

私有变量、方法是只读的,公有变量、方法是可读可写的

访问:


对于私有成员,直接访问即可,前面不用加任何修饰,


对于公有的访问在单体作用域内前面要加上“this.”,在单体作用域外前面要加上“circle.”(单体名字.)



呵呵,似乎有点味道了!


利用分支技术封装浏览器差异

.利用分支技术来来封装浏览器之间的差异


注意的地方:


a一定要用闭包,实现即时绑定


b每个分支之间用分号隔开


c最后返回的是分支的名字


d调用的时候用单体名+分支的方法名;


代码如下:

 
// 利用单体的分支技术来定义XHR(XMLHttpRequest)对象,必须要用闭包才可以实现
var XHR = (function(){
//The three branches
var standard = {
cXHR:function(){
return new XMLHttpRequest();
} 
}; 
var activeXNew = {
cXHR:function(){
return new ActiveXObject('Msxml2.XMLHttp');
} 
}; 
var activeXOld = {
cXHR:function(){
return new ActiveXObject('Microsoft.XMLHttp');
} 
}; 
//To assign(分配) the branch, try each method;return whatever doesn't fail
var testObject;
try{
testObject = standard.cXHR();
return standard;// return this branch if no error was thrown
}catch(e){
try{
testObject = activeXNew.cXHR();
return activeXNew;
}catch(e){
try{
testObject = activeXOld.cXHR();
return activeXOld;
}catch(e){
throw new Error('Create the XMLHttpRequestObject failed!');
} 
} 
} 
})(); 
window.onload = function(){
alert(XHR.cXHR());
} 

最后再啰嗦几句:


对于单体据说是最常用的模式之一了,至于利弊嘛要在实践中慢慢的体会了,由于本人也是初学,所以没有太多的发言权,不足指出还忘高手指教

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章