let命令

news/2024/5/21 0:16:32
  • let 命令
    • let 与 var
      • 二者区别:
        • 作用域不同
        • 变量提升(Hoisting)
        • 临时性死区
        • 重复声明
      • 联系:
      • 举例说明:
    • 块级作用域
      • 块级作用域的关键字
      • 使用 var(无块级作用域)
      • 使用 let(块级作用域)
      • 使用 const(块级作用域且常量)
    • 块级作用域与函数声明

1. let 命令

ES6 新增了 let 命令,用来声明变量。它的用法类似于 var ,但是所
声明的变量,只在 let 命令所在的代码块内有效。

{let a = 10;var b = 1;
}
a // ReferenceError: a is not defined.
b // 1

上面代码在代码块之中,分别用 let 和 var 声明了两个变量。然后在
代码块之外调用这两个变量,结果 let 声明的变量报错, var 声明的
变量返回了正确的值。

这表明, let 声明的变量只在它所在的代码块有效。

1.1. let 与 var

letvar 都是用来在 JavaScript 中声明变量的关键字,但它们之间存在一些关键区别:

1.1.1. 二者区别:
1.1.1.1. 作用域不同
  • var 声明的变量具有函数作用域全局作用域

如果在函数内部声明,它只在该函数内部可见;如果在函数外部声明,则在整个脚本中都可见,成为全局变量(在浏览器环境下,全局变量会成为 window 对象的属性)。

  • let 声明的变量具有块级作用域

这意味着变量只在它声明的那个代码块(例如,if 语句、for 循环或者一对大括号 {} 内)内有效。

1.1.1.2. 变量提升(Hoisting)
  • var 声明的变量会存在变量提升现象,即使声明在代码执行之后,变量也会被提升至作用域顶部,但在赋值前其值为 undefined

  • let 不会发生变量提升,如果在声明前访问 let 变量,会导致引用错误(ReferenceError)。

1.1.1.3. 临时性死区

当使用 letconst 声明变量时,在声明之前访问这些变量会触发错误,这个区域被称为临时死区。

这是为了防止变量在声明前的不确定状态,提高代码的可预测性。

if (true) {console.log(temp); // 报错,temp在TDZ中let temp = "ES6";
}

只要块级作用域内存在 let 命令,它所声明的变量就“绑定”(binding)
这个区域,不再受外部的影响。

var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}

上面代码中,存在全局变量 tmp ,但是块级作用域内 let 又声明了一
个局部变量 tmp ,导致后者绑定这个块级作用域,所以在 let 声明变
量前,对 tmp 赋值会报错。

ES6明确规定,如果区块中存在 let 和 const 命令,这个区块对这些
命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使
用这些变量,就会报错。

总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用
的。这在语法上,称为“暂时性死区”(temporal dead zone,简称
TDZ)。

if (true) {// TDZ开始tmp = 'abc'; // ReferenceErrorconsole.log(tmp); // ReferenceErrorlet tmp; // TDZ结束console.log(tmp); // undefinedtmp = 123;console.log(tmp); // 123
}

上面代码中,在 let 命令声明变量 tmp 之前,都属于变量 tmp 的“死
区”。

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量

1.1.1.4. 重复声明
  • 使用 var 可以在同一作用域内重复声明同一个变量,后面的声明会覆盖前面的声明。

  • let 不允许在同一作用域内重复声明同一个变量,尝试这样做会导致语法错误。

1.1.2. 联系:
  • 它们都是用于变量声明的关键词。
  • 在基本的变量赋值和访问操作上,两者的行为是类似的,一旦声明并赋值后,都可以用来存储数据。
1.1.3. 举例说明:
// var 示例
function varExample() {var x = 1;if (true) {var x = 2; // 这里改变了外层函数的xconsole.log(x); // 输出2}console.log(x); // 输出2,因为var变量提升了且被覆盖
}// let 示例
function letExample() {let y = 1;if (true) {let y = 2; // 这里创建了一个新的块级作用域变量yconsole.log(y); // 输出2}console.log(y); // 输出1,因为let有块级作用域
}varExample(); // 调用函数演示var行为
letExample(); // 调用函数演示let行为

在这个例子中,varExample 函数展示了使用 var 时变量提升以及在相同作用域内重复声明导致的变量覆盖现象。

letExample 函数则展示了 let 如何在块级作用域内创建独立的变量,避免了变量覆盖的问题。

ES6 规定暂时性死区和 let 、 const 语句不出现变量提升,主要是为
减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料
之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免
此类错误就很容易了。

1.2. 块级作用域

ES5 只有全局作用域和函数作用域,没有块级作用域。

ES6(ECMAScript 2015)引入了块级作用域,这是一种新的变量作用域规则,它允许变量在代码块(通常是一对大括号 {} 内)内声明和使用,超出这个块之后,这些变量就会被销毁或不再可见。

这与ES5中的作用域规则不同,ES5中只有全局作用域和函数作用域,没有块级作用域的概念。

实例一

var tmp = new Date();
function f() {console.log(tmp);if (false) {var tmp = 'hello world';}
}
f(); // undefined

实例二

var tmp = new Date();
function f() {console.log(tmp); 
}
f(); // Mon May 06 2024 09:44:26 GMT+0800 (中国标准时间)

实例一代码的原意是, if 代码块的外部使用外层的 tmp 变量,内部使
用内层的 tmp 变量。但是,函数 f 执行后,输出结果为 undefined

原因在于变量提升,导致内层的 tmp 变量覆盖了外层的 tmp 变量。

1.2.1. 块级作用域的关键字
  • let: 用于声明一个块级作用域的变量,它可以重复声明,但在同一作用域内不能重复初始化。
  • const: 用于声明一个块级作用域的常量,一旦赋值不能改变,也不能重新声明。
1.2.2. 使用 var(无块级作用域)

在ES5中,即使变量在某个代码块内声明,它也会被提升到包含它的函数作用域或全局作用域中。

var x = 1;
if (true) {var x = 2; // 这里的x会覆盖外层的x
}
console.log(x); // 输出2
1.2.3. 使用 let(块级作用域)

使用 let 声明的变量只在声明它的块内有效。

let y = 1;
if (true) {let y = 2; // 这个y只在这个if块内有效
}
console.log(y); // 输出1,外层的y不受影响
1.2.4. 使用 const(块级作用域且常量)

const 声明的变量同样遵循块级作用域,而且声明后其值不能被重新赋值。

if (true) {const z = 3; // z是一个常量,只能在此if块内访问// z = 4; // 这里会报错,尝试修改常量值
}
// console.log(z); // 这里也会报错,z在块外不可见

1.3. 块级作用域与函数声明

函数能不能在块级作用域之中声明?这是一个相当令人混淆的问题。

ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。

// 情况一
if (true) {function f() {}
}
// 情况二
try {function f() {}
} catch(e) {
// ...
}

上面两种函数声明,根据 ES5 的规定都是非法的。

在ES6(ECMAScript 2015)之前,JavaScript只有全局作用域和函数作用域。这意味着在函数外部声明的变量是全局变量,在函数内部声明的变量则是局部变量,仅在该函数内部可见。然而,对于if语句、for循环等代码块内部,并没有自己的作用域。

ES6引入了块级作用域,主要通过letconst关键字实现,这使得开发者可以在任何块(例如if语句、for循环的花括号内)内声明变量,这些变量的作用域仅限于该块。

关于函数声明在块级作用域中的行为,ES6规范试图明确函数声明应当具有块级作用域特性,即在块内声明的函数只应在该块内可访问。

理论上,这样的函数声明应该类似于使用let声明的变量,只在声明它们的块中可见。例如:

{function sayHello() {console.log("Hello");}sayHello(); // 此处可以调用
}
// sayHello(); // 理论上此处应该报错,因为sayHello只在上面的块内可见

但实际上,由于历史遗留原因和浏览器兼容性问题,早期的ES6实现中,函数声明在块级作用域的行为并不统一。

一些环境(特别是某些浏览器)可能仍然会将块级函数声明提升到包含块的顶部,或者有其他非标准行为。

因此,在实际开发中,为了避免潜在的问题,推荐在块级作用域内使用函数表达式而非函数声明:

{const sayHello = function() {console.log("Hello");};sayHello(); // 正确使用函数表达式
}
// sayHello(); // 这里依然会报错,因为sayHello是块级作用域内的函数表达式

总结来说,虽然ES6旨在让函数声明遵循块级作用域规则,但由于兼容性问题,最好在块级作用域中使用函数表达式以确保代码的可预测性和兼容性。


http://www.mrgr.cn/p/46440476

相关文章

【数学建模】天然肠衣搭配问题衍生问题/线性规划限制条件建立问题

线性规划限制条件建立问题 前景回顾/提出问题回顾1回顾2/问题提出解决前提 解决方法坐标轴(区间)法总结 前景回顾/提出问题 回顾1 首先回顾一下DVD在线租赁问题 在 question2中,需要保证每个人都不会收到自己不喜欢的DVD,即客户在线订单数为0时候&…

C#/.NET/.NET Core优秀项目和框架2024年4月简报

前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项…

c#word文档:3.向Word文档中插入表格/4.读取Word文档中表格

--向Word文档中插入表格-- (1)在OfficeOperator项目的WordOperator类中定义向Word文档插入换页的函数NewPage (2)在WordOperator类中定义向Word文档插入表格的函数InsertTable using Microsoft.Office.Interop.Word;// 引入Mic…

30分钟彻底了解Flutter整个渲染流程(超详细)

30分钟彻底了解Flutter整个渲染流程[超详细] 从运行第一行代码出发WidgetsFlutterBinding初始化了一堆娃 三个中流砥柱SchedulerBindingRendererBindingWidgetsBinding 申请Vsync流程下发Vsync承接Vsync 从运行第一行代码出发 void main() {runApp(const MyApp()); }void runA…

linux中进程相关概念(一)

什么是程序,什么是进程,有什么区别? 程序是静态的概念,当我们使用gcc xxx.c -o pro进行编译时,产生的pro文件,就是一个程序。 进程是程序的一次运行活动,通俗点就是说程序跑起来了就是进程。 …

C++反汇编,指针和内存分配细节,面试题05

文章目录 20. 指针 vs 引用21. new vs malloc 20. 指针 vs 引用 指针是实体,占用内存空间,逻辑上独立;引用是别名,与变量共享内存空间,逻辑上不独立。指针定义时可以不初始化;引用定义时必须初始化。指针的…

Vue自定义封装音频播放组件(带拖拽进度条)

Vue自定义封装音频播放组件(带拖拽进度条) 描述 该款自定义组件可作为音频、视频播放的进度条,用于控制音频、视频的播放进度、暂停开始、拖拽进度条拓展性极高。 实现效果 具体效果可以根据自定义内容进行位置调整 项目需求 有播放暂停…

localhost 重定向次数过多

在完成javaweb作业时出现了错误初始页面只有两个功能, 但是无论是点击登录还是注册,都会跳转到login.jsp页面从网上找到的答案是代码陷入死循环,因为总是跳转到login.jsp, 所以我查看了所有servlet类中跳转到login.jsp页面的代码,逻辑上并没有问题;然后我又查看了过滤器以…

Windows平台使用CMake+MinGW64编译OpenCV

Windows平台使用CMake+MinGW64编译OpenCV (注:2年前写的笔记, 可能有些地方过时了) 目录Windows平台使用CMake+MinGW64编译OpenCV1.安装及配置环境1.1 MinGW-w641.2 CMake1.3 OpenCV源码2.CMake配置及生成2.1 新建目录2.2 CMake-GUI2.3 编译配置2.4 生成2.5 Make编译和安装3.配…

【大模型赋能开发者】海云安入选数世咨询LLM驱动数字安全2024——AI安全系列报告

近日,国内知名数字产业领域第三方调研咨询机构数世咨询发布了LLM驱动数字安全2024——AI安全系列报告。报告通过调研、公开信息收集等方式对目前十余家已具备LLM相关的应用能力安全厂商对比分析出了这一领域当前的产业现状并进行了各厂商的能力展示。 海云安凭借近…

金融业开源软件应用 评估规范

金融业开源软件应用 评估规范 1 范围 本文件规定了金融机构在应用开源软件时的评估要求,对开源软件的引入、维护和退出提出了实现 要求、评估方法和判定准则。 本文件适用于金融机构对应用的开源软件进行评估。 2 规范性引用文件 下列文件中的内容通过文中的规范…

介绍适用于 Node.js 的 Elastic OpenTelemetry 发行版

作者:来自 Elastic Trent Mick 我们很高兴地宣布推出 Elastic OpenTelemetry Distribution for Node.js 的 alpha 版本。 该发行版是 OpenTelemetry Node.js SDK 的轻量级包装,可以让你更轻松地开始使用 OpenTelemetry 来观察 Node.js 应用程序。 背景 …

x64dbg中类似于*.exe+地址偏移

在CE和xdb中,形如*.exe数字偏移形式的地址被称为模块地址,CE附加到进程后点击查看内存,显示如下图 这种地址学名叫做模块地址,在x64dbg中显示如下图: CE中可以关闭,从而显示绝对的虚拟地址,如下…

时间复杂度空间复杂度 力扣:转轮数组,消失的数字

1. 算法效率 如何衡量一个算法的好坏?一般是从时间和空间的维度来讨论复杂度,但是现在由于计算机行业发展迅速,所以现在并不怎么在乎空间复杂度了下面例子中,斐波那契看上去很简洁,但是复杂度未必如此 long long Fib…

【JavaEE网络】HTTP响应详解:状态码、报头与正文的全面解析

目录 HTTP响应(Response)认识 "状态码" (status code)认识响应 “报头”(header)认识响应 “正文”(body) HTTP响应(Response) 响应: 首行响应头空行正文 认…

MySQL#MySql表的操作

目录 一、创建表 二、查看表结构 三、修改表 1.修改表的名字 2.新增一个列 3.修改列 4.删除列 5.修改列的名称 四、删除表 一、创建表 语法: CREATE TABLE table_name (field1 datatype,field2 datatype,field3 datatype ) character set 字符集 collate 校…

一键实现在VS Code中绘制流程图

VS Code是一款常用的IDE,受到许多用户的欢迎和喜爱。而其较为出众的一点,就是较好的可拓展性,即丰富的插件应用,这些应用可以极大地提高生产效率,并优化日常使用。 流程图是一种直观的图示方法,可以用简明…

jsp 实验12 servlet

一、实验目的 掌握怎样在JSP中使用javabean 二、实验项目内容&#xff08;实验题目&#xff09; 编写代码&#xff0c;掌握servlet的用法。【参考课本 上机实验1 】 三、源代码以及执行结果截图&#xff1a; 源代碼&#xff1a; inputVertex.jsp&#xff1a; <% page lang…

[转帖]TLAB(Thread Local Allocation Buffer)

https://www.cnblogs.com/Chary/p/18034613 TLAB是虚拟机在堆内存的eden划分出来的一块专用空间,是线程专属的。在虚拟机的TLAB功能启动的情况下,在线程初始化时,虚拟机会为每个线程分配一块TLAB空间,只给当前线程使用,这样每个线程都单独拥有一个空间,如果需要分配内存,…

【前端】CSS基础(1)

文章目录 前言一、CSS基础1、 CSS是什么2、 CSS基本语法规范3、 代码风格3.1 样式格式3.2 样式大小写3.3 空格规范 4、 CSS引入方式4.1 内部样式表4.2 行内样式表4.3 外部样式 前言 这篇博客仅仅是对CSS的基本结构进行了一些说明&#xff0c;关于CSS的更多讲解以及HTML、Javasc…