Null's Blog

Javascript中的闭包、模仿块级作用域和私有变量

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式为:在一个函数内部创建另一个函数。

“当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链。然后,使用arguments和其他命名参数的值来初始化函数的活动对象(activation object).但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象出于第三位。。。。。直至作用域链终点的全局执行环境。”

1
2
3
4
5
6
7
8
9
10
11
12
13
function creawteComparisonFunction(propertyName){
return function(object1,object2){
var value1=object1[propertyName];
var value2=object1[propertyName];
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
};
}

在匿名函数从createComparisonFunction()中被返回后,它的作用域链被初始化为包含createComparisonFunction()函数的活动对象和全局变量对象。更重要的是,createComparisonFunction()函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。或者说,当createComparisonFunction()函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然留在内存在,直到匿名函数被销毁后:

1
2
3
4
5
6
//创建函数
var compareNames=creawteComparisonFunction("name");
//调用函数
var result=compareName({name:"gay1"},{name:"gay2"});
//解除对匿名函数的引用(以便释放内存)
compareNames=null;

请看如下代码:

1
2
3
4
5
6
7
8
9
function createFunctions(){
var result=new Array();
for(var i=0;i<10;i++){
result[i]=function(){
return i;
};
}
return result;
}

首先得确定:这段代码创建了10个闭包,但结果却不是我们想要的,要达到目的,可以这样:

1
2
3
4
5
6
7
8
9
10
11
function createFunctions(){
var result=new Array();
for(var i=0;i<10;i++){
result[i]=function(num){
return function(){
return num;
};
}(i);
}
return result;
}

在这个版本中,没有直接把闭包复制给数组,而是定义了一个匿名函数,并将立即执行改匿名函数的结果赋值给数组。这里的匿名函数有一个参数num,也就是最终的函数要返回的值。在调用每个匿名函数时,我们传入了变量i。由于函数参数是按值传递的,所以就会将变量i的当前值复制给参数num。而在这个匿名函数内部,又创建并返回。了一个反问num的闭包。这样一来,result数组中的每个函数都有自己num变量的一个副本,因此就可以返回各自不同的数值了。
“javascript没有块级作用域的概念。” 但是可以使用“私有作用域”的匿名函数语法来打到目的:

1
2
3
4
5
6
7
8
function outputNumber(count){
(function(){
for(var i=0;i<count;i++){
alert(i);
}
})();
alert(i); //报错, 但是如果不实用私有作用域,则正常
}

“私有变量”大家一定比我更了解这个,这里就不谈了。注意一个坑就好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function Person(name){
this.getName=function(){
return name;
};
this.setName=function(value){
name=value;
};
}
var person1=new Person("gay1");
alert(person1.getName());
var person2=new Person("gay2");
alert(person2.getName());
person1.setName("gay3");
alert(person1.getName()); //gay3
alert(person2.getName()); //gay2
/**************邪恶的分割线***********************/
(function(){
var name="";
Person=function(value){
name=value;
};
Person.prototype.getName=function(){
return name;
};
Person.prototype.setName=function(value){
name=value;
};
})();
var person1=new Person("gay1");
alert(person1.getName());
person1.setName("gay2");
alert(person1.getName());
var person2=new Person("gay3");
alert(person1.getName()); //gay3
alert(person2.getName()); //请注意这里是gay3

结论

其实闭包除了在面试的时候,基本没什么卵用,不知道什么什么时候开始,这似乎成了面试必问的问题。
实际上在日常开发过程正,养成良好的编码规范,利用好闭包不污染全局变量的特性即可。