三、模仿块级作用域
如前所述,JS
没有块级作用域的概念。下面通过例子说明:
function outputNumbers(count){
for (var i=0; i < count; i++){
alert(i);
}
alert(i); //count
}
说明:这个函数如果在Java
等语言中,变量i
只会在for
循环的语句块中有定义,循环一旦结束,变量i
就会被销毁。但是在JS
中,变量i
是定义在outputNumbers()
活动对象中的,因此从它有定义开始,就可以在函数内部随处访问它,即使像下面这样的错误也不会改变它:
function outputNumbers(count){
for (var i=0; i < count; i++){
alert(i);
}
var i; //variable re-declared
alert(i); //count
}
说明:JS
从来不会告诉你是否多次声明了同一个变量。遇到这种情况,它只会对后续的声明视而不见(不过,它会执行后续声明中的变量初始化)。匿名函数可以用来模仿块级作用域并避免这个问题。用作块级作用域(通常称为私有作用域)的匿名函数的语法如下:
(function(){
//这里是块级作用域
})();
说明:切记不要像下面这样使用:
function(){
//这里是块级作用域
}();//出错
因为JS
将function
关键字当作一个函数声明的开始,而函数声明后面不能跟圆括号。因为声明的匿名函数被立即调用,不仅能访问全局活动对象,并且闭包在执行完之后立即被销毁,这样便和块级作用域类似了。这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。
四、私有变量
严格来讲,JS
中没有私有成员的概念。所有对象属性都是公有的。不过,倒是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
function add(num1, num2){
var sum = num1 + num2;
return sum;
}
说明:这个函数中有三个私有变量:num1、num2、sum
。在函数外部不能访问,但是可以在函数内部创建一个闭包访问。我们把有权访问私有变量和私有函数的公有方法称为特权方法。有两种在对象上创建特权方法的方式。第一种是在构造函数中定义特权方法:
function MyObject(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权方法
this.publicMethod = function(){
privateVariable++;
return privateFunction();
};
}
说明:在创建MyObject
的实例后,除了使用publicMethod()
这个途径外,没有任何方法可以直接访问privateVarialbe
和privateFunction()
。我们可以在构造函数中定义Setter
和Getter
方法,这样便不能直接访问相关变量,而只能通过Setter
和Getter
方法访问。
4.1 静态私有变量
通过在私有作用域中定义私有变量胡函数,同样也可以创建特权方法,其基本模式如下所示。
(function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//构造函数
MyObject = function(){};
//公有/特权方法
MyObject.prototype.publicMethod = function(){
privateVariable++;
return privateFunction();
};
})();
说明:这个模式在定义构造函数时并没有使用函数声明,而是使用了函数表达式。函数声明只能创建局部函数,但那并不是我们想要的。同样,我们也没有在声明MyObject
时使用var
关键字(初始化未经声明的变量,总是会创建一个全局变量)。于是,MyObject
就成了一个全局变量,能够在私有作用域之外被访问到(严格模式下会导致错误)。
这个模式和之前的模式最主要的区别就是私有变量和函数是实例共享的。由于特权方法是在原型上定义的,因此所有实例都使用同一个函数。以这种方式创建,则相关变量就称为静态的了,可以增加代码的复用性。具体使用哪一种需要视情况而定。
4.2 模块模式
前面的模式是为自定义类型创建私有变量和特权方法的,而模块模式则是为单例创建私有变量和特权方法。JS
是以对象字面量的方式来创建单例对象的。
var singleton = {
mane : value,
method : function(){
//这里是方法代码
}
};
模块模式通过为单例添加私有变量和特权方法能够使其得到增强,其语法如下:
var singleton = function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权/公有方法和属性
return {
publicProperty : true,
publicMethod : function(){
privateVariable++;
return privateFunction();
}
};
}();
说明:这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的。如:
var application = function(){
//私有变量和私有函数
var components = new Array();
//初始化
components.push(new BaseComponent());
//公共
return{
getComponentCount : function(){
return components.length;
},
registerComponent : function(component){
if(typeof component == "object"){
components.push(component);
}
}
};
}();
说明:在Web
应用程序中,经常需要使用一个单例来管理应用程序级的信息。返回对象的两个方法,前者返回已注册组件数目,后者用于注册新组件。
4.3 增强的模块模式
可以进一步改进模块模式,即在返回对象之前加入对其增强的代码。这种增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况。
var application = function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//创建对象
var object = new CustomType();
//添加特权/公有属性和方法
object.publicProperty = true;
object.publicMethod = function(){
privateVariable++;
return privateFunction();
};
//返回这个对象
return object;
}();
如果前面演示的模块模式的例子中的application
对象必须是BaseComponent
的实例,那么就可以使用如下代码:
var application = function(){
//私有变量和私有函数
var components = new Array();
//初始化
components.push(new BaseComponent());
//创建application 的一个局部副本
var app = new BaseComponent();
//公共接口
app.getComponentCount = function(){
return components.length;
};
app.registerComponent = function(component){
if(typeof component == "object"){
components.push(component);
}
};
//返回这个副本
return app;
}();