以下解释均为个人理解,有错误之处的话欢迎提出,能给出建议的话万分感激!
在闭包中,for循环是一个经典实例。
例1:
for(var i=1;i<=5;i++){
setTimeout(function timer(){
document.write(i);
},1000);
}
//输出5个6
解释:本意是想通过循环输出12345,但是因为js中for循环没有块级作用域,即迭代产生的i都在全局作用域里,每次迭代后i被改变,而延迟函数的回调会在for循环结束后执行(该知识为异步执行(我还未学习。。),可见http://cnodejs.org/topic/5401dfb79769c2e93756216b),即五个延迟函数在循环结束后执行,因为闭包的原因,延迟函数获取全局的i(即i=6),所以输出5个6
例2:
(function (){
setTimeout(function timer(){
document.write(i);
},1000);
})();
}
//输出5个6
解释:虽然使用了IIFE(但IIFE是空的,即只有延迟函数),但是每次执行的延迟函数因为闭包的原理,还是从全局获取i,所以仍然输出5个6
例3:
for(var i=1;i<=5;i++){
(function (j){
setTimeout(function timer(){
document.write(j);
},1000);
})(i);
}
//输出12345
for(var i=1;i<=5;i++){
(function (){
var j=i;
setTimeout(function timer(){
document.write(j);
},1000);
})();
}
//输出12345
解释:例3和例4原理一样。因为IIFE中传入参数,即IIFE中有定义变量j;因为IIEF每次都会创建一个作用域,j在该作用域保存着每次迭代的i的值,而延迟函数会将IIEF每次创建的作用域封闭起来(即闭包),然后延迟函数等循环结束后执行,通过闭包,执行延迟函数时记得并能访问每次迭代的作用域。
例5:
for(var i=1;i<=5;i++){
let j=i;
setTimeout(function timer(){
document.write(j);
},1000);
}
//输出12345
for(var i=1;i<=5;i++){
var j=i;
setTimeout(function timer(){
document.write(j);
},1000);
}
//输出5个5
解释:let的使用,劫持形成块级作用域,为什么要用块级作用域呢?就如同例3例4的IIEF的函数作用域一样。详细原理同上。注:例5的let的使用会使j声明5次
for(let i=1;i<=5;i++){
setTimeout(function timer(){
document.write(i);
},1000);
}
//输出12345
解释:由于let的使用,形成块级作用域,而且由于对i使用let声明,所以就相当于使该for循环变成块级作用域,且只需要声明一次。
对上述的作用域和延迟函数的‘纠缠’:
我理解为,当直接使用for循环时,因为闭包,相当于执行5次在for循环外的延迟函数
使用IIFE和let时,循环结束后执行延迟函数,通过延迟函数的闭包,能访问其函数(或块)级作用域(因为闭包的原因保留着),所以可以输出每次迭代的值。
在闭包中,for循环是一个经典实例。
例1:
for(var i=1;i<=5;i++){
setTimeout(function timer(){
document.write(i);
},1000);
}
//输出5个6
解释:本意是想通过循环输出12345,但是因为js中for循环没有块级作用域,即迭代产生的i都在全局作用域里,每次迭代后i被改变,而延迟函数的回调会在for循环结束后执行(该知识为异步执行(我还未学习。。),可见http://cnodejs.org/topic/5401dfb79769c2e93756216b),即五个延迟函数在循环结束后执行,因为闭包的原因,延迟函数获取全局的i(即i=6),所以输出5个6
例2:
IIFE是指(function foo(){....})()的模式
(function (){
setTimeout(function timer(){
document.write(i);
},1000);
})();
}
//输出5个6
解释:虽然使用了IIFE(但IIFE是空的,即只有延迟函数),但是每次执行的延迟函数因为闭包的原理,还是从全局获取i,所以仍然输出5个6
例3:
for(var i=1;i<=5;i++){
(function (j){
setTimeout(function timer(){
document.write(j);
},1000);
})(i);
}
//输出12345
for(var i=1;i<=5;i++){
(function (){
var j=i;
setTimeout(function timer(){
document.write(j);
},1000);
})();
}
//输出12345
解释:例3和例4原理一样。因为IIFE中传入参数,即IIFE中有定义变量j;因为IIEF每次都会创建一个作用域,j在该作用域保存着每次迭代的i的值,而延迟函数会将IIEF每次创建的作用域封闭起来(即闭包),然后延迟函数等循环结束后执行,通过闭包,执行延迟函数时记得并能访问每次迭代的作用域。
例5:
for(var i=1;i<=5;i++){
let j=i;
setTimeout(function timer(){
document.write(j);
},1000);
}
//输出12345
for(var i=1;i<=5;i++){
var j=i;
setTimeout(function timer(){
document.write(j);
},1000);
}
//输出5个5
解释:let的使用,劫持形成块级作用域,为什么要用块级作用域呢?就如同例3例4的IIEF的函数作用域一样。详细原理同上。注:例5的let的使用会使j声明5次
for(let i=1;i<=5;i++){
setTimeout(function timer(){
document.write(i);
},1000);
}
//输出12345
解释:由于let的使用,形成块级作用域,而且由于对i使用let声明,所以就相当于使该for循环变成块级作用域,且只需要声明一次。
对上述的作用域和延迟函数的‘纠缠’:
我理解为,当直接使用for循环时,因为闭包,相当于执行5次在for循环外的延迟函数
使用IIFE和let时,循环结束后执行延迟函数,通过延迟函数的闭包,能访问其函数(或块)级作用域(因为闭包的原因保留着),所以可以输出每次迭代的值。