function fun(){
for(var i=0; i<lis.length; i++){ //此处的length=5
lis[i].onclick = function(){
console.log(i);
}
}
}
复制代码
因而我在console里写入了如上代码,依次点击lis,输出了五次4,这对于写惯了c语言的我是一个观念上的颠覆,因而开始了大规模的资料查找,试图解决个人这个疑惑。html
"在这个函数里面的i其实引用的是最后一次i的值,为何不是1,2,3,4...呢? 由于for循环中并无执行这个函数,这个函数是在你点击的时候才执行的,当执行这个函数的时候,它发现它本身没有这个变量i,因而向它的做用域链中查找这个变量i,由于当你单击这个box的时候已经for循环完了,因此找到的i是最后一次赋值后的i"es6
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
var funcs = createFunctions();
for (var i=0; i < funcs.length; i++){
console.log(funcs[i]());
}
复制代码
陷阱就是:函数带()才是执行函数! 单纯的一句 var f = function() { alert('Hi'); }; 是不会弹窗的,后面接一句 f(); 才会执行函数内部的代码。上面代码翻译一下就是:bash
var result = new Array(), i;
result[0] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!
result[1] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!
...
result[9] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!
i = 10;
funcs = result;
result = null;
console.log(i); // funcs[0]()就是执行 return i 语句,就是返回10
console.log(i); // funcs[1]()就是执行 return i 语句,就是返回10
...
console.log(i); // funcs[9]()就是执行 return i 语句,就是返回10
复制代码
"为何只垃圾回收了 result,但却不收了 i 呢? 由于 i 还在被 function 引用着啊。比如一个餐厅,盘子老是有限的,因此服务员会去巡台回收空盘子,但还装着菜的盘子他怎么敢收? 固然,你本身手动倒掉了盘子里面的菜(=null),那盘子就会被收走了,这就是所谓的内存回收机制。"闭包
“做用域链的机制引出了一个值得注意的反作用,即闭包只能取得包含函数中任何变量的最后一个值。”函数
“表面上看,每一个函数都应该返回本身对应的i值,但实际上每一个函数都返回了同样的值。由于每一个函数的做用域链中都保存着fun()函数的活动对象,因此他们引用的都是同一个变量i。当fun()函数返回后,变量的i值是4,此时每一个函数都引用着保存变量i的同一个变量对象,因此在每一个函数内部i的值都是10。”ui
function fun(){
for(var i=0; i<lis.length; i++){ //此处的length=5
lis[i].onclick = (function(num){
return function(){
console.log(num);
}
})(i)
}
}
复制代码
这种方法在每次循环中,用当即执行的匿名函数记录下了当前的i值(num),并建立了单独的做用域,又在匿名函数中建立了一个新的闭包,接收i(num)值,造成了单独的做用域链。spa
function fun(){
for(let i=0; i<lis.length; i++){ //此处的length=5
lis[i].onclick = function(){
console.log(i);
}
}
}
复制代码
此方法的成功,最大的功臣即是let的块级做用域特色,他在每次循环中生成了单独的做用域,达到了与上一种方法相同的效果。翻译
研究这个看似很简单的特性耗费了整整一天的时间,也深深体会到了为何说JS语言的糟粕很多。设计