http://edu.51cto.com/lesson/id-6390.html
本文主要讲匿名函数中的私有化(私有作用域,私有变量)的问题
一、模仿块级作用域
1.块级作用域(也叫私有作用域)
function box(){
for(var i=0; i<5; i++){ //for语句为块级作用域(但是在这里不是块级作用域,因为JS没这个东西)
}
alert(i);
}
box(); //输出5 说明for语句里用完之后 没有销毁
function box(){
for(var i=0; i<5; i++){
}
var i; //就算重新声明,也不会影响之前声明初始化的数据 这里若声明并初始化 var i=1000,则最后输出的是1000
alert(i);
}
box(); //输出5
以上两个例子说明,javascript没有块级作用域。if(){} for(){}等没有作用域,如果有,出了这个范围 i 就应该呗销毁了。就算重新声明同一个变量也不会改变它的值。
js不会提醒你是否多次声明了同一个变量。遇到这种情况,它只会对后续的声明视而不见(如果初始化了,当然还会执行的)。使用模仿块级作用域可避免这个问题。
2.使用块级作用域(私有作用域)
(达到目的:上面的例子,除了for语句,i 就销毁掉了)
function box(){
(function(){ //包含自我执行的匿名函数,就可以实现私有作用域
for(var i=0; i<5; i++){
alert(i); //输出5次 0 1 2 3 4
}//这里面用了a b c等变量
})(); //出了这个私有作用域,变量立即被销毁 a b c被销毁
//这里可以继续使用a b c等变量,和上面的a b c完全没有联系
alert(i); //这里就不认识了 not defined
}
box();
使用了块级作用域(私用作用域)后,匿名函数中定义的任何变量,都会在执行结束时被销毁。这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。一般来说,我们都应该尽可能少向全局作用域中添加变量和函数。在大项目中,多人开发的时候,过多的全局变量和函数容易导致命名冲突,引起灾难性的后果。如果采用块级作用域(私有作用域),每个开发者既可以使用自己的变量,又不比担心搞乱全局作用域。
我们经常看到js文件(用jQUery写的)这样写
(function($) {
这里是我们的逻辑。这里可以形成块级作用域,这里定义的任何变量,和方法。而这些变量和方法,在全局作用域中是访问不到的。私有化,保护局部变量。
})(jQuery);
这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。这句话理解如下:
程序1
var age=100;
alert(age);//输出100
age=null;
alert(age);//输出null
程序2
(function(){
//这里就是全局的私有作用域
var age=100;
alert(age);
})(); //页面加载,自行执行函数,输出100
alert(age);// age is not defined 即,外部访问不到私有作用域里的age,这里就不用手动添加age=null,age就会自动销毁。
在全局作用域中使用块级作用域可以减少闭包占用的内存问题,因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域链了。
3.私有变量
javascript没有私有属性的概念,所有的对象属性都是公有的。不过,却有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能再函数外部访问这些变量。
function box(){
var age=100; //私有变量,外部无法访问
}
function Box(){
this.age = 100; //属性 下面能box.age可以正确输出,说明是公有的
this.run = function(){ //方法 下面能box.run可以正确输出,说明是公有的
return '运行中';
}
}
var box=new Box();
alert(box.age);//输出 100
alert(box.run());//输出 运行中
而通过函数内部创建一个闭包,那么闭包通过自己的作用域也可以访问这些变量,利用这一点,可以创建用于访问私有变量的公有方法。
function Box(){
var age=100; //私有变量
function run(){ //私有函数
return'运行中';
}
this.publicGo= function(){ //对外可见的公共接口,特权方法
return age+run();
};
}
var box = new Box();
alert(box.publicGo());
//通过构造函数传参 来访问私有变量
function Box(value){
var user=value; //私有变量
this.getUser=function(){
return user;
};
this.setUser= function(){
user=value;
}
}
var box = new Box('lee');
alert(box.getUser()); //输出lee
box.setUser('ooo'); //重新赋值
alert(box.getUser()); //输出ooo
但是对象的方法,在多次调用的时候,会多次创建。可以使用静态私有变量来避免这个问题。
静态私有变量
通过块级作用域中定义私有变量或函数,同样可以查ungjianduiwai公共的特权方法。
//
(function(){
var user ='' // 私有变量
//function Box(){} //构造函数,但是在函数里面写构造函数,不支持,因为私有作用域里的函数,外部无法访问
Box=function(value){
user=value;
}; //全局,构造函数 里面没有var 就说明是全局的
Box.prototype.getUser = function(){ //使用prototype导致方法共享了,而user变成了静态属性。
return user;
};
Box.prototype.setUser= function(value){
use=value;
}
})();
var box = new Box('lee');//第一次实例化
alert(box.getUser()); //输出lee
var box2 = new Box('kkk');//第二次实例化
alert(box.getUser()); //输出kkk 实现了共享
box2. setUser('OOO');
alert(box.getUser()); //输出OOO 实现了共享
4.模块模式
之前采用的都是构造函数的方式来创建私有变量和特权方法,那么对象字面量方式就采用模块模式来创建。这里采用对象字面两方式来创建私有变量和特权方法。
什么叫单例,就是永远只实例化一次,其实就是字面量声明方式
var box={ //第一次实例化,无法第二次实例化,那么就是单例
user :'lee',
run : function(){
return '行中';
};
//字面量方式 私有化变量和函数
var box = function(){
var age=100; //私有变量
function run(){ //私有函数
return'运行中';
}
return { //直接返回对象
go:function(){ //对外公共借口的特权方法
return age+run();
}
};
}();//自我执行的函数
alert(box.go()); //输出 100运行中
上面直接返回对象的例子也可以这么写
var box = function(){
var age=100; //私有变量
function run(){ //私有函数
return'运行中';
}
var obj= { //直接返回对象
go:function(){ //对外公共借口的特权方法
return age+run();
}
};
return obj;
}();//自我执行的函数
alert(box.go()); //输出 100 运行中
字面量的对象声明,其实在设计模式中可以看作是一种单例模式,所谓单例模式,就是永远保持对象的一个实例。
增强的模块模式,这种模式适合返回自定义对象,也就是构造函数
//之前的两个例子都是返回的{} 也就是object,如果想返回自定义的呢?例如Desk
function Desk(){};
var box = function(){
var age = 100;
function = run(){
return'运行中';
}
var desk = new Desk();
desk.go = function(){
return age+run();
}
return desk;
}();
alert(box.go());