一文讲清楚for循环内的块级作用域和闭包原理

前言

大家好,for循环应该是老生常谈的问题了,但我今天还是想再回顾一下,探索其中原理的同时,也能学到一些有用的知识。下文将通过局部变量、块级作用域、闭包三种方式来展示哪种方法可以解决for循环存在的缺陷,并且说明为什么 不能解决以及为什么 能解决,并且会演示在浏览器中如何进行打断点及如何查看代码运行生成的局部变量、块级作用域以及闭包。

关键词: for循环、局部变量、块级作用域、闭包

for循环探索

for循环使用var声明变量的缺陷

js 复制代码
for(var i = 0; i <= 2; i++) {
  setTimeout(() => {
    console.log(i) // 打印三次3
  }, 1000 * i)
}

执行后,会每秒打印一个3,这是因为在延时器回调函数执行前,for循环已经结束,这时i=3。

而延时器的回调函数在没有执行前,并不知道i的具体值是什么,始终保留的是对i的引用。

当回调函数执行时,去读取i的值,这是发现i已经是3了,所以打印结果为三次3。

创建局部变量探索

js 复制代码
for(var i = 0; i <= 2; i++) {
  var cb = () => {
    const j = i  // 每次循环保存i的值
    console.log(j) // 打印三次3
  }
  setTimeout(cb, 1000 * i)
}

我们在浏览器的sources标签下打开运行的代码文件,在22行处打了个断点,运行后可以看到右边Scope的Local下可看到局部变量j始终是3,由于没有形成Closure(闭包),所以回调函数里给j赋值的i始终是引用回调函数执行时i的具体值,也就是循环结束后i的值,所以并没有真正解决问题。

每次循环重新var声明变量

js 复制代码
for(var i = 0; i <= 2; i++) {
  var j = i
  setTimeout(() => {
    console.log(j) // 打印三次2
  }, 1000 * j)
}
console.log(j) // 2

for循环处声明的var变量相对于声明在全局作用域window.j(确切来说是在for循环外的作用域),每次循环只是简单的赋值,并不用解决问题。

使用let创建块作用域

js 复制代码
for(var i = 0; i < 3; i++) {
  // let隐式声明块级作用域
  let j = i
  setTimeout(() => {
    console.log(j) // 0,1,2
  }, 1000 * i)
  // 等价于let显示声明块级作用域
  // {
  //   let j = i
  //   setTimeout(() => {
  //     console.log(j) // 0,1,2
  //   }, 1000 * i)
  // }
}

在Scope下可以看到形成了一个块级作用域,下面保存着j变量。每次执行会分别打印0,1,2,所以通过块级作用域可以解决for循环的缺陷。

使用let创建块作用域(简写版)

js 复制代码
for(let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i) // 0,1,2
  }, 1000 * i)
}

使用IIFE(立即执行函数)创建闭包

js 复制代码
for(var i = 0; i < 3; i++) {
  (function(j){
    console.log(i)  // 0,1,2
  })(i)
}

相当于每次执行循环都会创建一个闭包 ,j保留着对当次循环的i的具体值的引用。

在Scope(作用域)下的Closure(闭包)处可看到j形成了闭包。

所以通过闭包也能解决问题。

总结

解决for循环缺陷的方法可以使用let块级作用域 或者闭包 的方式。注意 区分局部作用域和块级作用域是不同的概念,局部变量并不能真正解决问题。

往期文章回顾

uniapp实现背景颜色跟随图片主题色变化(多端兼容)

一文学会请求中断、请求重发、请求排队、请求并发

table表格自适应浏览器窗口变化解决方案

一文学会vue3如何自定义hook钩子函数和封装组件

相关推荐
Penge6661 小时前
HTML基础:HTML 标签属性
前端
Penge6661 小时前
HTML 基础:块级元素、行内元素与行内块元素详解
前端
Penge6662 小时前
前端基础:html语义化
前端
用户30816998764852 小时前
同一个端口能被两个程序监听吗?TCP 11 组绑定实测
前端
deli0072 小时前
红绿灯配时实测:固定 60 秒 vs 自适应,2400 辆车差多少
前端
去伪存真2 小时前
井道无信号、满手黑机油,我们如何用 DeepSeek V4 打造不“犯浑”的电梯维保 Agent?
前端·agent
deli0072 小时前
60fps 为什么还是一顿一顿?帧时间曲线实测给你看
前端
时间的情敌2 小时前
Cesium 流动管线、管道动画、管线流向仿真,管网、油气、水利孪生高频需求
前端·交互·cesium
yuyuxun12 小时前
协同过滤的校园二手物品智能推荐系统的设计与实现 毕业设计-附源码40082
javascript·vue.js·spring boot·python·数据分析·django·课程设计
wangxiaoxuan2174 小时前
SLAM 的基本结构:前端、后端、回环与地图优化
前端·机器人·科沃斯八界