【ES6 教程】第一章 新的ES6语法—let:使用let关键字声明块范围的变量

ES6教程1年前 (2023)更新 admin
1,822 0

前言

ECMAScript 2015 或 ES2015 是对 JavaScript 编程语言的重大更新。这是自 2009 年标准化的 ES5 以来该语言的第一次重大更新。因此,ES2015 通常被称为 ES6。

要学习本 ES6 教程,我们应该对 ES5 之前的 JavaScript 有很好的了解。

正文

今天这篇文章是ES6系列教程的第一篇,在今天的本教程中,我们将学习如何使用 JavaScript let 关键字来声明块范围的变量。

JavaScript let 关键字简介

在 ES5 中,当我们使用 var 关键字声明一个变量时,变量的作用域要么是全局的,要么是局部的。如果在函数之外声明变量,则变量的范围是全局的。在函数内声明变量时,变量的作用域是局部的。

ES6 提供了一种使用 let 关键字声明变量的新方法。let 关键字类似于 var 关键字,只是这些变量是块作用域的。例如:

let variable_name;

在 JavaScript 中,块用花括号 {} 表示,例如 if else、for、do while、while、try catch 等:

if(condition) {
// inside a block
}

请参阅以下示例:

let x = 10;
if (x == 10) {
let x = 20;
console.log(x); // 20: reference x inside the block
}
console.log(x); // 10: reference at the begining of the script

脚本的工作原理:

  • 首先,声明一个变量x并将其值初始化为 10。
  • 其次,x在if块内声明一个同名的新变量,但初始值为 20。
  • 第三,输出块x内和if块后 变量的值。

因为let关键字声明了一个块范围的变量,块x内的变量if是一个新变量,它隐藏了x在脚本顶部声明的变量。因此,x控制台中的值为20。

当 JavaScript 引擎完成执行if块时,块x内的变量if超出范围。因此, 块x后面的变量的if 值为 10。

JavaScript let 和全局对象

当您使用var关键字声明一个全局变量时,您将该变量添加到全局对象的属性列表中。对于 Web 浏览器,全局对象是window. 例如:

它的工作原理:

首先,声明一个变量 x 并将其值初始化为 10。

其次,在 if 块中声明一个同名 x 的新变量,但初始值为 20。

第三,输出 if 块内部和之后的变量 x 的值。

因为 let 关键字声明了一个块范围的变量,所以 if 块中的 x 变量是一个新变量,它会隐藏在脚本顶部声明的 x 变量。因此,控制台中 x 的值为 20。

当 JavaScript 引擎完成执行 if 块时, if 块内的 x 变量超出范围。因此,if 块后面的 x 变量的值为 10。

JavaScript let 和全局对象

当我们使用 var 关键字声明全局变量时,我们将该变量添加到全局对象的属性列表中。在 Web 浏览器的情况下,全局对象是窗口。例如:

var a = 10;
console.log(window.a); // 10

但是,当我们使用 let 关键字声明变量时,该变量不会作为属性附加到全局对象。例如:

let b = 20;
console.log(window.b); // undefined

for循环中的JavaScript let和回调函数

请参阅以下示例。

for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}

代码的目的是每秒向控制台输出 0 到 4 的数字。但是,它会输出五次数字 5:

5
5
5
5
5

在这个例子中,变量i是一个全局变量。在循环之后,它的值为5。当回调函数被传递给setTimeout()函数执行时,它们引用i与值5相同的变量。

在 ES5 中,您可以通过创建另一个作用域来解决此问题,以便每个回调函数引用一个新变量。并且要创建一个新的作用域,您需要创建一个函数。通常,您可以按如下方式使用IIFE模式:

在这个例子中,变量 i 是一个全局变量。在循环之后,它的值为 5。当回调函数被传递给 setTimeout() 函数执行时,它们引用了相同的变量 i,值为 5。

在 ES5 中,我们可以通过创建另一个范围来解决此问题,以便每个回调函数引用一个新变量。而要创建一个新的范围,我们需要创建一个函数。通常,我们按如下方式使用 IIFE 模式:

for (var i = 0; i < 5; i++) {
(function (j) {
setTimeout(function () {
console.log(j);
}, 1000);
})(i);
}

输出:

0
1
2
3
4

在 ES6 中,let 关键字在每次循环迭代中声明一个新变量。因此,我们只需将 var 关键字替换为 let 关键字即可解决此问题:

for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}

要使代码完全 ES6 风格,可以使用如下箭头函数:

for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000);
}

请注意,我们将在后面的教程中了解有关箭头函数的更多信息。

重新声明

var 关键字允许我们重新声明变量而不会出现任何问题:

var counter = 0;
var counter;
console.log(counter); // 0

但是,使用let关键字重新声明变量将导致错误:

let counter = 0;
let counter;
console.log(counter);

这是错误消息:

Uncaught SyntaxError: Identifier ‘counter’ has already been declared

JavaScript let 变量和提升

让我们检查以下示例:

{
console.log(counter); //
let counter = 10;
}

此代码导致错误:

Uncaught ReferenceError: Cannot access ‘counter’ before initialization

在此示例中,counter在声明变量之前访问该变量会导致ReferenceError. 我们可能认为使用let关键字的变量声明不会提升,但会提升。

实际上,JavaScript 引擎会将let关键字声明的变量提升到块的顶部。但是,JavaScript 引擎不会初始化该变量。因此,当你引用一个未初始化的变量时,你会得到一个ReferenceError.

暂时死亡区(TDZ)

由let关键字声明的变量具有所谓的时间死区 (TDZ)。TDZ 是从块开始到处理变量声明的时间。

以下示例说明时间死区是基于时间的,而不是基于位置的。

在此示例中,在声明计数器变量之前访问它会导致 ReferenceError。 我们可能认为使用 let 关键字声明的变量不会提升,但确实如此。

事实上,JavaScript 引擎会将 let 关键字声明的变量提升到块的顶部。但是,JavaScript 引擎不会初始化该变量。因此,当我们引用一个未初始化的变量时,我们会得到一个 ReferenceError。

时间死亡区 (TDZ)

由 let 关键字声明的变量具有所谓的时间死区 (TDZ)。TDZ 是从块开始到处理变量声明的时间。

以下示例说明时间死区是基于时间的,而不是基于位置的。

{ // enter new scope, TDZ starts
let log = function () {
console.log(message); // messagedeclared later
};

// This is the TDZ and accessing log
// would cause a ReferenceError

let message= ‘Hello’; // TDZ ends
log(); // called outside TDZ
}

在这个例子中:

首先,花括号开始一个新的块作用域,因此,TDZ 开始。

其次,log() 函数表达式访问消息变量。但是,log() 函数尚未执行。

第三,声明消息变量并将其值初始化为10。从块作用域开始到消息变量被访问的时间称为临时死区。当 JavaScript 引擎处理声明时,TDZ 结束。

最后,调用访问 TDZ 之外的消息变量的 log() 函数。

请注意,如果我们访问由 TDZ 中的 let 关键字声明的变量,我们将得到一个 ReferenceError,如下例所示。

{ // TDZ starts
console.log(typeof myVar); // undefined
console.log(typeof message); // ReferenceError
let message; // TDZ ends
}

注意 myVar 变量是一个不存在的变量,因此它的类型是未定义的。

临时死亡区可防止您在声明之前意外引用变量。

总结

使用 let 关键字声明的变量是块作用域的,不会初始化为任何值,也不会附加到全局对象。

使用 let 关键字重新声明变量会导致错误。

使用 let 关键字声明的变量的时间死区从块开始,直到初始化被评估。

最后,感谢你的阅读,如果你觉得今天内容不错,请记得分享给你身边做开发的朋友,也能能够帮助到他。

 

英文 | https://www.javascripttutorial.net

翻译 | 杨小爱

© 版权声明

相关文章

暂无评论

暂无评论...