在这以前先要了解一下javascript
var 是函数级做用域或者全局做用域,let是块级做用域 看一个例子html
function foo() {
for (var index = 0; index < array.length; index++) {
//..循环中的逻辑代码
}
console.log(index);//=>5
}
foo()
console.log(index)//Uncaught ReferenceError: index is not defined
复制代码
foo函数下的index输出5,全局下的index不存在 如今咱们把var 换为letjava
function foo() {
for (let index = 0; index < array.length; index++) {
//..循环中的逻辑代码
}
console.log(index)//Uncaught ReferenceError: index is not defined
}
foo()
复制代码
报错了,index不在foo函数做用域下,固然确定也不会再全局下 由于var和let的这个区别(固然var和let的区别不止于此)因此致使了下面的这个问题 关于var的面试
const array = [1, 2, 3, 4, 5]
function foo() {
for (var index = 0; index < array.length; index++) {
setTimeout(() => {
console.log(index);
}, 1000);
}
}
foo()
复制代码
const array = [1, 2, 3, 4, 5]
function foo() {
for (let index = 0; index < array.length; index++) {
setTimeout(() => {
console.log(index);
}, 1000);
}
}
foo()
复制代码
setTimeout(func,time)是在time(毫秒单位)时间后执行func函数。浏览器引擎按顺序执行程序,遇到setTimeout会将func函数放到执行队列中,等到主程序执行完毕以后,才开始从执行队列(队列中可能有多个待执行的func函数)中按照time延时时间的前后顺序取出来func并执行。即便time=0,也会等主程序运行完以后,才会执行。闭包
上面的let是循环打印了12345,可是不是间隔1s打印的,是在foo函数执行1s后,同时打印的异步
function foo(){
let index = 0;
const array = [1, 2, 3, 4, 5]
const t = setInterval(()=>{
if (index < array.length) {
console.log(array[index]);
}
index++;
}, 1000);
if (index >= array.length) {
clearInterval(t);
}
}
foo()
复制代码
咱们上面说到,当for循环遇到了var,变量index的做用域在foo函数下,循环一次赋值一次,5次循环完成,index最后的结果赋值就为5;就是被最终赋值的index,就是5;函数
代码执行顺序是,先同步执行for循环,再执行异步队列,在for循环执行完毕后,异步队列开始执行以前,index通过for循环的处理,变成了5。 因此咱们引入一个全局变量j,使j在for循环执行完毕后,异步队列开始执行以前,依然是0,在异步执行时进行累加优化
var j = 0;
for (var index = 0; index < array.length; index++) {
setTimeout(() => {
console.log(j);
j++;
}, 1000 * index)
}
复制代码
const array = [1, 2, 3, 4, 5]
function foo() {
for (let index = 0; index < array.length; index++) {
setTimeout(() => {
console.log(index);
}, 1000*index);
}
}
foo()
复制代码
开始讨论方式四以前我推荐先阅读一遍我以前写过一篇文章 谈一谈javascript做用域 咱们对上面的问题再次分析,for循环同步执行,在for循环内部遇到了setTimeout,setTimeout是异步执行的,因此加入了异步队列,当同步的for循环执行完毕后,再去执行异步队列,setTimeout中有惟一的一个参数数index 方式三可行,是由于let是块级做用域,每次for执行都会建立新的变量index,for循环执行完毕后,异步执行以前,建立了5个独立的做用域,5个index变量,分别是0,1,2,3,4,相互独立,互不影响,输出了预期的结果 若是说每次循环都会生成一个独立的做用域用来保存index,问题就会获得解决,因此,咱们经过闭包来实现
const array = [1, 2, 3, 4, 5]
function foo() {
for (var index = 0; index < array.length; index++) {
function fun(j) {
setTimeout(function () {
console.log(j);
}, 1000 * j);
}
fun(index)
}
}
foo()
复制代码
setTimeout中的匿名回调函数中引用了函数fun中的局部变量j,因此当fun执行完毕后,变量j不会被释放,这就造成了闭包 固然咱们能够对此进行一下优化
const array = [1, 2, 3, 4, 5]
function foo() {
for (var index = 0; index < array.length; index++) {
(function(j) {
setTimeout(function () {
console.log(j);
}, 1000 * j);
})(index)
}
}
foo()
复制代码
将foo函数改成匿名的当即执行函数,结果是相同的
for循环自己是同步执行的,当在for循环中遇到了异步逻辑,异步就会进入异步队列,当for循环执行结束后,才会执行异步队列 当异步函数依赖于for循环中的索引时(必定是存在依赖关系的,否则不会再循环中调动异步函数)要考虑做用域的问题, 在ES6中使用let是最佳的选择, 当使用var时,能够考虑再引入一个索引来替代for循环中的索引,新的索引逻辑要在异步中处理 也可使用闭包,模拟实现let 在实际开发过程当中,循环调用异步函数,比demo要复杂,可能还会出现if和else判断等逻辑,具体的咱们下次再续
参考
《你不知道的JavaScript》上卷