如何在外部引用一个函数内部的变量?
//===============================
function a(){
var i = 1;
}
alert(i); //undifined
//===============================
为什么这样?因为一个变量的作用域只在一个函数本身,这是javascript最基本的入门的概念,想来不用多说。
//===============================
function a(){
var i=0;
function b(){
alert(++i);
}
return b;
}
var c = a();
c(); //1;
//===============================
这个例子来自于百度百科,有两个关键点
1. b是定义在a的内部
2. a的运行结果是 return b。此时b就一直保存在内存当中,直到你手动删除为止。
3. b引用了a的变量(这一点极其重要)。
所以,c = a();其实调用的就是b,由于b是在a的内部,所以就可以访问i(这是javascript的链接作用域(chain scope),内部函数可以访问外部函数的变量)。
这样,就达到了在a的外部,调用a中的变量。
再有,a中定义的函数是b,而不是一个匿名函数,但是这样是可行的。所以,上面第4条的定义就是错的,闭包并不一定是一个匿名函数。
这一个例子,基本就已经说完了闭包了。但是看的人可能还是没有一个明确的概念,那么就继续看几个例子。
//===============================
function a(){
var i = 1;
function b(){
alert(i);
}
i++;
return b;
}
var f = a();
f(); // 2
为什么呢是2呢?
return b是在a的最后执行,所以,在ruturn b的时候,已经将a中代码全部执行了,所以 i=2;若在i++前返回b。f()的结果就是1。这就可以推出:也可以把 var i = 1;写在function b的后面。只要写在return之前即可。
function a(){
var i = 1;
function b(){alert(i)}
return b;
i++;
}
//===============================
//===============================
function a(){
var i = 1;
b = function(){alert(i)}
c = function(){i++}
d = function(j){i = j}
}
a(); //运行一次a,为b\c\d赋值。
b(); // 1
c(); // i++
b(); // 2
d(3); // i = 3
b(); //3
此处应有三点需要说明:
1. 在函数内部定义变量一定要用var,否则定义的就是一个全局变量。在这里b c d皆没有用var,所以就可以在外部直接调用这三个全局变量
2. a需要运行一次,才会给bcd赋值。否则会报错 b c d未定义
3. b c d 都在a内,所以都可以调用变量i.
//===============================
循环中的闭包
//===============================
function closureInLoop(Ar){
var result = [];
for(var i=0;i<Ar.length;i++){
var item = ‘item‘ + Ar[i];
result.push(function(){alert(item + ‘ ‘ + Ar[i])});
}
return result;
}
function test(){
var fnList = closureInLoop([1,2,3]);
alert(fnList);
for(var j=0;j<fnList.length;j++){
fnList[j]();
}
}
test(); // item3 undifined(3次);
我原以为,会依然出现item 1,item 2,item 3。结果却不同,这是什么原因?
先把fnList输出来看看[fnction(){alert(item + ‘ ‘ + Ar[i])},function(){alert(item + ‘ ‘ + Ar[i])},function(){alert(item + ‘ ‘ + Ar[i])},]
三个一模一样的function,都是调用的Ar[i]。此时i是多少呢?看下前面的closureInLoop的for循环。
传入的参数是[1,2,3],所以closureInLoop中的Ar.length就是3。所以for执行完的时候:
i=3;
item = ‘item‘ + Ar[2];也就是item3;
由于return result了。所以此时resulte里面的function就形成了闭包。
三个function都引用了i和item。此时i=3,item = item3;
所以都是function(){item + ‘ ‘ + Ar[3]}
但是Ar传入的是[1,2,3],Ar.length = 3 没错。
但是Ar[0] = 1;Ar[1] = 2; Ar[2] = 3....都没错。
但是骚年们,Ar[3] = undifined;
alert(item + ‘ ‘ + Ar[3]) = item3 undifined。
Over,这个太麻烦,我想也没几个人这样写吧。不过这个例子非常经典,可以仔细看看。
例外一种循环里的闭包
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i);
},1000);
}
这样并不能依次输出i,而是输出10次10。
//===================================
每次都创建一个新的闭包
//===================================
function createClosure(number,reference){
var num = number,
ref = reference,
anArray = [1,2,3];
return function(x){
num += x;
anArray.push(num);
alert(num + ‘ ‘ + anArray.toString() + ‘ ‘ + ref);
}
}
closure_1 = createClosure(20,‘ref_1‘);
closure_1(10); //30 [1,2,3,30] ref_1
closure_2 = createClosure(100,‘ref_2‘);
closure_2(-10); //90 [1,2,3,90] ref_2
说好的闭包呢,为什么没有继续用closure_1的nunber和reference?
因为我们每建立一个新的闭包,都重新将number和reference的通过参数改变。
//===============================
参考资料
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
http://jibbering.com/faq/notes/closures/
http://en.wikipedia.org/wiki/Closure_(computer_programming)
http://baike.baidu.com/view/648413.htm
http://coolshell.cn/articles/6731.html
http://bonsaiden.github.io/JavaScript-Garden/zh/