闭包与变量的经典实例
一、闭包导致变量泄露为全局变量
function CreatFunction(){
var result=[];
// var j=5;
for (var i =0; i <10; i++) {
result[i]=function() {
return i;
};
}
return result;
}
CreatFunction()
也就是高程书上说的:闭包只能取得包含函数中任何变量的最后一个值。
原因:
下图是该函数的运行结果,result=包含了10个函数的数组
而这个数组的每一个f()={return i;}
也就是说,内部仅仅声明某一个函数,引擎并不会对函数内部的任何变量进行查找或赋值操作。只会对函数内部的语法错误进行检查,也就是说,这个f()并没有执行,结果只是把函数返回了,此时如果进一步让函数去执行,那么根据闭包的原理,一:因为是闭包,内部函数调用了createfunction作用域里面的i,所以i不会消失;二:根据作用域链,内部函数会去外部作用域也就是CReatfunction()作用域里面寻找i,并赋值给内部函数,当然此时,定义的函数已经运行完毕了,也就是for循环已经运行完了,此时i=10,所以运行结果如下图:
二、该问题的修正
1、用立即执行函数进行修正
高程书上是这么修改的:创建另一个匿名函数强制让闭包的行为符合预期
function CreatFunction(){
var result=[];
// var j=5;
for (var i =0; i <10; i++) {
result[i] = (function (val) {
return function () {
return val;
};
})(i);
}
return result;
}
CreatFunction();
为了保证返回值也是个函数,所以在第5行加了一个立即执行函数,这样得到的结果也是一个包含十个函数的数组
但与上一个不同的是,该函数没有把闭包直接赋给数组,而是用了一个匿名立即执行函数嵌套起来,相当于给内部函数function
function() {return val; }
加了一个块级作用域,它仍然是闭包,但是因为函数function(val)是按值传递的,所以i会赋值给val这个形参,让函数function(val)把内部函数包起来,不让val泄露到全局中,也就可以保证,数组依次执行结果是0123456789
2、用let进行修正
let有块级作用域,因此就相当于4-8行被包含进了for的这个块级作用域中,从而把i锁定。
function CreatFunction(){
var result=[];
// var j=5;
for (let i =0; i <10; i++) {
result[i]=function() {
return i;
};
}
return result;
}
CreatFunction()
运行结果如下:
简要图示:
我尽力了,再写下去就疯了