一、let在循环中的特殊性

let作为ES6引入的块级作用域声明,在循环结构中存在特殊行为,其核心区别于var的函数作用域特性。理解这一特性对于编写正确的闭包逻辑至关重要。

在 ECMAScript 规范里,let声明的变量具有块级作用域特性,这彻底改变了 JavaScript 原有的作用域规则。在 ES6 之前,JavaScript 只有全局作用域和函数作用域,var声明的变量在函数内是共享的,这在循环结合闭包的场景下容易引发问题。而let的出现填补了块级作用域的空白,为开发者提供了更细粒度的作用域控制。

二、循环头声明:每次迭代创建独立作用域

letfor循环头部声明变量时,JavaScript引擎会为每次迭代创建独立的块级作用域,并将变量绑定到该作用域中。即使循环体为空,这种作用域隔离机制依然生效。

示例代码

const arr = [];  
for (let i = 0; i < 2; i++) {  arr.push(() => console.log(i));  
}  
arr[0](); // 输出:0(捕获第一次迭代的i)  
arr[1](); // 输出:1(捕获第二次迭代的i)  

执行机制

  1. 第一次迭代:创建作用域Scope1i初始值为0,闭包捕获Scope1中的i
  2. 第二次迭代:创建新作用域Scope2i初始值基于前次迭代为1,闭包捕获Scope2中的i
  3. 闭包调用:分别访问各自作用域中的i值,输出01

在ECMAScript 2024 规范的 14.7.5 The for Statement 章节的 LetAndConstDeclarationInForInitializer 部分明确指出:当let或const声明出现在for循环的头部时,会被特殊处理。每次循环迭代都会为let或const声明的变量创建一个新的词法环境,变量的初始值取自前一次迭代的环境。这确保了在循环体中创建的闭包会捕获它们创建时所在迭代的变量值,而非循环结束后的最终值。

NOTE 2
When a let or const declaration occurs in the head of a for loop, it is interpreted specially. Each iteration of the loop creates a new lexical environment for the variables declared with let or const, and the initial value of the variable is taken from the previous iteration’s environment. This ensures that closures created within the loop body capture the variable’s value from the iteration in which they were created, rather than the value after the loop completes.

三、循环体声明:基于代码块的作用域创建

let在循环体内部声明变量时,每次执行循环体代码块{}会创建新的块级作用域,变量被绑定到该作用域。

示例代码

const arr = [];  
for (var i = 0; i < 2; i++) {  let j = i; // 每次迭代创建新作用域  arr.push(() => console.log(j));  
}  
arr[0](); // 输出:0  
arr[1](); // 输出:1  

关键区别

  • 循环头的let i是引擎特殊优化,自动为每次迭代创建作用域。
  • 循环体的let j依赖代码块结构,每次执行循环体时创建作用域。

这种在循环体中基于代码块创建作用域的方式,遵循let声明变量的块级作用域基本规则。只要代码执行进入包含let声明的代码块,就会创建新的作用域,将声明的变量限制在该代码块内。在循环场景下,每次循环执行循环体这个代码块时,自然也会为let声明的变量创建新作用域。

四、与var的对比:共享全局作用域导致的闭包陷阱

使用var声明变量时,所有闭包共享同一个全局作用域中的变量,导致捕获的是循环结束后的最终值。

示例代码

const arr = [];  
for (var i = 0; i < 2; i++) {  arr.push(() => console.log(i));  
}  
arr[0](); // 输出:2(循环结束时i=2)  
arr[1](); // 输出:2  

原因分析

  • var的函数作用域特性导致整个循环中只有一个i
  • 闭包捕获的是全局作用域中的i,循环结束时其值为2

在 ES6 之前,由于var声明变量的函数作用域特性,在循环中创建闭包时,闭包捕获的是共享的全局作用域中的变量。这就导致在循环结束后,所有闭包访问到的变量值都是循环结束时该变量的最终值,无法获取到每次迭代时变量的不同值。

五、特殊场景:循环体内部修改块级变量

若在循环体内部修改let声明的变量,闭包将捕获修改后的值。

示例代码

const arr = [];  
for (var i = 0; i < 2; i++) {  let j = i;  j++; // 修改块级变量  arr.push(() => console.log(j));  
}  
arr[0](); // 输出:1(第一次迭代j=0+1)  
arr[1](); // 输出:2(第二次迭代j=1+1)  

执行机制

  • 每次迭代创建新作用域,j初始化为i,修改后闭包捕获新值。

当在循环体内部修改let声明的变量时,因为每次迭代都有独立的作用域,修改的是当前作用域内的变量。闭包捕获的正是所在作用域内变量修改后的最终值,所以会输出修改后的值。

六、总结:闭包与作用域的交互规则

  1. 闭包捕获变量引用:闭包捕获的是变量的引用,而非创建闭包时变量的值。
  2. let的块级作用域:在循环头或循环体中使用let,会为每次迭代/执行创建独立作用域。
  3. var的函数作用域:所有闭包共享同一个变量,导致捕获最终值。

这一特性是 ES6 对 JavaScript 作用域机制的重要改进,避免了传统闭包陷阱,使代码逻辑更符合直觉。从规范层面看,let在循环中的这些特性有明确的定义和规则,从实际应用角度,这些特性为开发者编写可靠、易维护的 JavaScript 代码提供了有力支持。无论是在前端开发中处理 DOM 事件绑定,还是在复杂的 JavaScript 应用程序中管理变量作用域和闭包逻辑,理解和运用好let在循环中的作用域机制都至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/916173.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/916173.shtml
英文地址,请注明出处:http://en.pswp.cn/news/916173.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

@Subscribe@AllowConcurrentEvents解析这两个注解

@Subscribe@AllowConcurrentEvents解析这两个注解 @Subscribe 和 @AllowConcurrentEvents 是 Guava EventBus(Google 开源的事件总线框架)中用于处理事件订阅的注解,主要用于实现组件间的解耦通信。下面分别解析: 1. @Subscribe 注解 作用:标记一个方法为事件订阅者方法,…

好看的小程序推广单页HTML源码 可用作导航页

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 响应式的小程序推广单页HTML源码。这个设计采用了现代化的UI元素&#xff0c;包含吸引人的标题、特性展示、二维码区域和行动号召按钮。 二、效果展示 1.部分代码 代码如下&#xff0…

华为仓颉编程语言实践体验

华为仓颉编程语言实践体验 目前华为仓颉编程语言因为其推出时间较短&#xff0c;生态系统不完善。官网资料权威&#xff0c;但比较庞大难懂。快速实验入门&#xff0c;是学习一门编程语言的法宝。网上靠谱的资料稀少&#xff0c;特此撰文介绍&#xff0c;帮助初学者减少挫折感&…

YOLOv11实战,使用YOLOv11训练自己的数据集和推理(附YOLOv11网络结构图)

2024年计算机视觉领域的颠覆性突破,YOLOv11以22%的参数量减少和0.3%的mAP提升重新定义实时目标检测的边界 本文将手把手带你完成YOLOv11的全流程实战,包含环境配置、数据准备、模型训练、推理部署及创新优化方案,并深度解析其网络架构设计思想。 一、YOLOv11核心创新解析 …

macOS xcode打包ios测试ipa应用包

可以参考&#xff1a; https://blog.csdn.net/sinat_34104446/article/details/133684756 过程中遇到很多稀奇古怪的报错&#xff0c;基本重启电脑即可解决。。。在我按照上面的步骤申请并导入新证书后&#xff0c;还遇到了一个问题&#xff1a;解决办法&#xff1a; https://b…

STM32基础知识学习笔记:ICODE、DCODE、DMA等常见名词的解释

基于AI生成内容。 ICODEICODE&#xff1a;指令总线&#xff08;Instruction Bus&#xff09; 主要用于处理 CPU 对程序指令的读取操作。它是 STM32 存储架构中重要的组成部分&#xff0c;与数据总线&#xff08;DCODE&#xff09;、系统总线&#xff08;System Bus&#xff09;…

谁将统治AI游戏时代?腾讯、网易、米哈游技术暗战

游戏行业的“产能天花板”正被AI技术轰然击穿。腾讯、网易、米哈游……所有的游戏厂商都在押注AI&#xff0c;腾讯混元发布混元游戏视觉生成平台&#xff0c;分钟级生成高精度游戏角色&#xff1b;网易《蛋仔派对》借AI实现UGC创作平民化&#xff1b;米哈游新作更以实时多模态对…

基于springboot的工商局商家管理系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

ABP VNext + Razor 邮件模板:动态、多租户隔离、可版本化的邮件与通知系统

&#x1f680; ABP VNext Razor 邮件模板&#xff1a;动态、多租户隔离、可版本化的邮件与通知系统 &#x1f4da; 目录&#x1f680; ABP VNext Razor 邮件模板&#xff1a;动态、多租户隔离、可版本化的邮件与通知系统&#x1f31f; 一、TL;DR&#x1f4c8; 二、系统流程图…

疯狂星期四第19天运营日记

网站运营第19天&#xff0c;点击观站&#xff1a; 疯狂星期四 crazy-thursday.com 全网最全的疯狂星期四文案网站 运营报告 今日访问量 今日访问量42&#xff0c;疯狂之后的冷静&#xff0c;落差太大~~ 今日搜索引擎收录情况 必应仍然是24条记录&#xff0c;无变化 百度0收…

康养休闲旅游服务虚拟仿真实训室:赋能人才培养的创新路径

在康养休闲旅游行业数字化转型与职业教育改革的双重驱动下&#xff0c;康养休闲旅游服务虚拟仿真实训室已成为连接课堂教学与岗位实践的关键枢纽。它通过虚拟仿真技术重构康养服务场景&#xff0c;为学生打造沉浸式实践平台&#xff0c;在人才培养模式创新中发挥着不可替代的作…

python办自动化--读取邮箱中特定的邮件,并下载特定的附件

系列文章目录 python办公自动化–数据可视化&#xff08;pandasmatplotlib&#xff09;–生成条形图和饼状图 python办公自动化–数据可视化&#xff08;pandasmatplotlib&#xff09;–生成折线图 python办公自动化–数据可视化&#xff08;pandas读取excel文件&#xff0c;m…

清理DNS缓存

Cloudflarehttps://1.1.1.1/purge-cacheGooglehttps://dns.google/cacheOpenDNShttps://cachecheck.opendns.comLinux DNS缓存sudo systemd-resolve --flush-caches 或 sudo /etc/init.d/nscd restartWindows DNS缓存ipconfig /flushdnsmacOS DNS缓存sudo dscacheutil -flushca…

用 Python 写你的第一个爬虫:小白也能轻松搞定数据抓取(超详细包含最新所有Python爬虫库的教程)

用 Python 写你的第一个爬虫&#xff1a;小白也能轻松搞定数据抓取&#xff08;超详细包含最新所有Python爬虫库的教程&#xff09; 摘要 本文是一篇面向爬虫爱好者的超详细 Python 爬虫入门教程&#xff0c;涵盖了从基础到进阶的所有关键技术点&#xff1a;使用 Requests 与…

openmv识别数字

Lenet是一种卷积识别网络,可以用来识别打印的&#xff0c;或者是手写的数字利用NCC的模板匹配算法来进行数字识别&#xff0c;模板匹配需要我们事先保存需要匹配的数字以及字母的模板图片,模板匹配对于模板的大小和角度&#xff0c;有一定的要求如果数字的大小和角度有所变换&a…

一款功能全面的文体场所预约小程序

大家好👋 ,我是 阿问学长!专注于分享优质开源项目解析、计算机学习资料推荐,并为同学们提供毕业设计项目指导支持,欢迎关注交流!🚀 项目概述 随着全民健身的普及,各地新建了大批体育、健身、文化娱乐场所,中小学校园的运动设施也开始对市民开放。为了合理安排主办…

PyTorch中实现早停机制(EarlyStopping)附代码

1. 核心目的 当模型在验证集上的性能不再提升时&#xff0c;提前终止训练防止过拟合&#xff0c;节省计算资源 2. 实现方法 监控验证集指标&#xff08;如损失、准确率&#xff09;&#xff0c;设置耐心值&#xff08;Patience&#xff09; 3. 代码&#xff1a; class EarlySto…

Nacos-服务注册,服务发现(一)

nacos快速入手 Nacos是Spring Cloud Alibaba的组件, Spring Cloud Alibaba遵循Spring Cloud中定义的服务注册, 服 务发现规范. 因此使⽤Nacos和使⽤Eureka对于微服务来说&#xff0c;并没有太⼤区别. 主要差异在于&#xff1a; Eureka需要⾃⼰搭建⼀个服务, Nacos不⽤⾃⼰搭…

单片机(STM32-ADC模数转换器)

一、基础知识1. 模拟信号&#xff08;Analog Signal&#xff09;定义&#xff1a;模拟信号是连续变化的信号&#xff0c;可以取任意数值。特点&#xff1a;幅值和时间都是连续的&#xff0c;没有“跳变”。举例&#xff1a;声音&#xff08;麦克风采集到的电压&#xff09;温度…

side.cpp - OpenExo

side.cpp构造函数源代码run_side - 核心read_data()源代码FSR压力传感器读取与赋值步态事件检测&#xff1a;落地&#xff08;ground_strike&#xff09;步态周期自适应&#xff1a;期望步长更新Toe-Off/Toe-On事件检测与站立/摆动窗口更新步态百分比进度估算FSR阈值动态读取&a…