0

0

简单了解JavaScript变量or循环中的var和let

WBOY

WBOY

发布时间:2022-09-14 17:18:44

|

1769人浏览过

|

来源于脚本之家

转载

本篇文章给大家带来了关于javascript的相关知识,其中主要介绍了javascript变量or循环中的var和let详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,下面一起来看一下,希望对大家有帮助。

简单了解JavaScript变量or循环中的var和let

【相关推荐:javascript视频教程web前端

在for循环中使用var声明初始化带来的问题

// 一道经典面试题:
var funcs = [];
for (var i = 0; i < 3; i++) {
  funcs[i] = function() {
    console.log("My value: " + i)
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();
}
/*
输出结果:
> My value: 3
> My value: 3
> My value: 3
*/

会出现这种现象的原因就是:

  • var声明的作用域是函数作用域而不是块级作用域,因此在for循环的循环体之外依然能访问到在初始化for循环时定义的var变量。
  • 且在循环结束后访问时,访问到的var变量是已经完成循环后的值。

解决方法

使用闭包

ES5时代的解决办法就是通过IIFE创建一个闭包,把变量在函数体内保存起来,再执行函数时就不会去访问外层的var变量了。

立即学习Java免费学习笔记(深入)”;

var funcs = [];
for (var i = 0; i < 3; i++) {
    // 1. 闭包
    funcs[i] = (function (i) {
        return function () {
            console.log("My value: " + i);
        };
    })(i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}

使用let变量初始化

let声明是块级作用域,循环体内的变量不会泄露到块语句之外。

因此在循环结束后再去访问变量i时,没有外层作用域变量的干扰,访问到的自然就是函数体内保存下来的变量值。

var funcs = [];
// 2. let
for (let i = 0; i < 3; i++) {
  funcs[i] = function() {
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();
}

从这里也可以看出,使用var来初始化for循环本身就是违反直觉的。

用来初始化for循环的变量理应是for循环的局部变量,在循环结束以后这个变量就应该没有意义了才对。

但是如果使用var来初始化,由于var声明的变量的作用域是函数作用域,这个初始化变量就和for循环处于同一作用域了,不受for循环的限制。

本应是for循环的局部变量,却暴露在了和for循环同层的作用域,且变量值已经被循环次数改变,自然会影响循环结束后其他代码对该变量的访问。

而如果使用let来初始化for循环,就不会有这个困扰了,因为let声明的作用域是块级作用域,这个初始化变量会如愿成为for循环的局部变量。

for循环怎么处理用let和var声明的初始化变量?

先上结论:

  • 用var初始化时,for循环会直接使用创建的var初始化变量;
  • 用let初始化时,圆括号会自成一个作用域,for循环会将圆括号内的变量值往循环体内传递。

首先看第一个结论规范是这么说的: 

可以看到,规范对于var初始化变量没有什么特别的处理,直接就拿来用了。 此时这个变量就是个普通的var变量,和for循环处于同一作用域。

我们用代码来佐证一下:

var funcs = [];
for (var i = 0; i < 3; i++) {
    // !!!重复声明了一个同名的var变量
    var i = 5;
    console.log("My value: " + i);
}
/*
只会输出一次:
> My value: 5
*/

var可以重复声明且值会覆盖,因此在循环体内再声明一个var i = 5,循环变量被作没了,会直接跳出for循环。

var funcs = [];
for (var i = 0; i < 3; i++) {
    // 用let声明了一个和循环变量同名的变量
    let i = 5;
    console.log("My value: " + i);
}
/*
一共输出了3次:
> My value: 5
> My value: 5
> My value: 5
*/

初始化var变量在函数作用域,循环体内的let变量在块作用域,循环体内优先访问块作用域里的let变量,因此循环体内的i值会被覆盖。

又由于var变量实际上处于let变量的外层作用域,因此let变量没有重复声明,不会报错;var变量也会如期完成自己作为循环变量的使命。

再看第二个结论,同样是先看规范:

很明显可以发现,使用let来初始化会比使用var多了一个叫perIterationLets的东西。

perIterationLets是什么?

轻幕
轻幕

轻幕是一个综合性短视频制作平台,诗词、故事、小说等一键成片转视频,让内容传播更生动!

下载

从规范上可以看到,perIterationLets来源于LexicalDeclaration(词法声明)里的boundNames

而这个LexicalDeclaration(词法声明),其实就是我们用来初始化的let声明。

可以理解为,如果我们用let声明来初始化for循环,for循环内部不会像直接使用var变量一样来直接使用let变量,而是会先把let变量收集起来,以某种形式转换为perIterationLets,再传递给循环体。

perIterationLets被用来做什么的?

从规范上可以看到,我们的let变量以perIterationLets的身份,作为参数被传进了ForBodyEvaluation,也就是循环体里。

在循环体里,perIterationLets只做了一件事情,那就是作为CreatePerIterationEnvironment的参数:

从字面上理解,CreatePerIterationEnvironment意思就是每次循环都要创建的环境

要注意,这个环境不是{...}里的那些执行语句所处的环境。 {...}里的执行语句是statement,在规范里可以看到,stmt有自己的事情要做。

这个环境是属于圆括号的作用域,也就是我们定义的let初始化变量所在的作用域。

再看看每次循环都要创建的环境被用来干嘛了:

逐步分析一下方法:CreatePerIterationEnvironment这个

  • 首先,把当前执行上下文的词法环境保存下来,作为lastIterationEnv(上一次循环时的环境)
  • 创建一个和lastIterationEnv同级的新作用域,作为thisIterationEnv(本次循环的环境);
  • 遍历我们定义的let初始化变量,也就是perIterationLets,在thisIterationEnv(本次循环的环境)里创建一个同名的可变绑定,找到它们在lastIterationEnv(上一次循环时的环境)里的终值,作为这个同名绑定的初始值;
  • 最后,将thisIterationEnv(本次循环的环境)交还给执行上下文。

简而言之就是,for循环会在迭代之前创建一个和初始化变量同名的变量,并使用之前迭代的终值将这个变量初始化以后,再交还给执行上下文

用伪代码理解一下这个过程就是:

到这里又有一个问题,既然把圆括号内的变量向循环体里传递了,那如果在循环体里又重复声明了一个同名变量,算不算重复声明,会不会报错?

答案是不会。

因为CreatePerIterationEnvironment在执行时,在新环境里创建的是一个可变的绑定,因此如果在循环体内重复声明一个名字为i的变量,只是会影响循环体内执行语句对i值的访问。

var funcs = [];
for (let i = 0; i < 3; i++) {
    // !!!用let声明了一个和循环变量同名的变量
    let i = 5;
    console.log("My value: " + i);
}
/*
一共输出了3次:
> My value: 5
> My value: 5
> My value: 5
*/

总结

在for循环中使用var声明来初始化的话,循环变量会暴露在和for循环同一作用域下,导致循环结束后还能访问到循环变量,且访问到的变量值是经过循环迭代后的值。

解决这个问题的方法如下:

  • 使用闭包将循环变量的值作为局部变量保存起来;
  • 使用ES6的let声明,将循环变量的作用域限制在for循环内部,初始化变量始终是for循环的局部变量,不能在外界被访问到。

for循环是怎么处理用let和var声明的初始化变量的?

  • 用var初始化时,for循环会直接使用创建的var初始化变量;
  • 用let初始化时,圆括号会自成一个作用域,for循环会将圆括号内的变量值往循环体内传递。

【相关推荐:javascript视频教程web前端

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
es6新特性
es6新特性

es6新特性有:1、块级作用域变量;2、箭头函数;3、模板字符串;4、解构赋值;5、默认参数;6、 扩展运算符;7、 类和继承;8、Promise。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

106

2023.07.17

es6新特性有哪些
es6新特性有哪些

es6的新特性有:1、块级作用域;2、箭头函数;3、解构赋值;4、默认参数;5、扩展运算符;6、模板字符串;7、类和模块;8、迭代器和生成器;9、Promise对象;10、模块化导入和导出等等。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

195

2023.08.04

JavaScript ES6新特性
JavaScript ES6新特性

ES6是JavaScript的根本性升级,引入let/const实现块级作用域、箭头函数解决this绑定问题、解构赋值与模板字符串简化数据处理、对象简写与模块化提升代码可读性与组织性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

222

2025.12.24

go语言闭包相关教程大全
go语言闭包相关教程大全

本专题整合了go语言闭包相关数据,阅读专题下面的文章了解更多相关内容。

137

2025.07.29

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

2

2026.01.29

java配置环境变量教程合集
java配置环境变量教程合集

本专题整合了java配置环境变量设置、步骤、安装jdk、避免冲突等等相关内容,阅读专题下面的文章了解更多详细操作。

2

2026.01.29

java成品学习网站推荐大全
java成品学习网站推荐大全

本专题整合了java成品网站、在线成品网站源码、源码入口等等相关内容,阅读专题下面的文章了解更多详细推荐内容。

0

2026.01.29

Java字符串处理使用教程合集
Java字符串处理使用教程合集

本专题整合了Java字符串截取、处理、使用、实战等等教程内容,阅读专题下面的文章了解详细操作教程。

0

2026.01.29

Java空对象相关教程合集
Java空对象相关教程合集

本专题整合了Java空对象相关教程,阅读专题下面的文章了解更多详细内容。

3

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.3万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号