let命令

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'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 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; // 这里改变了外层函数的x
    console.log(x); // 输出2
  }
  console.log(x); // 输出2,因为var变量提升了且被覆盖
}

// let 示例
function letExample() {
  let y = 1;
  if (true) {
    let y = 2; // 这里创建了一个新的块级作用域变量y
    console.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旨在让函数声明遵循块级作用域规则,但由于兼容性问题,最好在块级作用域中使用函数表达式以确保代码的可预测性和兼容性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端布道人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值