ECMAScript 6 (ES6) 简介
ECMAScript 6(通常称为 ES6 或 ES2015),是 JavaScript 语言的一个重要版本,在2015年发布。它引入了许多新的语法特性,以简化代码编写并提高代码质量。其中最显著的变化之一就是对变量声明方式的改进:新增了let和const关键字,用来替代旧版JavaScript中唯一的变量声明方式var。
var 关键字
在 ES6 之前,JavaScript 中的变量主要是通过var关键字来声明的。var声明的变量具有函数作用域或全局作用域,并且存在变量提升的现象,这意味着无论变量是在函数内部的哪个位置声明的,它们都会被视为在函数的顶部被声明。并且,var没有块级作用域的概念,即使在一个if语句或for循环中声明,它的作用域仍然会延伸到整个函数或全局环境。
作用域
{
var blockScoped = "I'm block scoped";
}
function exampleFunction() {
var functionScoped = "I'm function scoped";
if (true) {
var innerVariable = "This is also function scoped!";
}
console.log(blockScoped); // "I'm block scoped"
console.log(innerVariable); // "This is also function scoped!"
}
exampleFunction();
console.log(functionScoped); // functionScoped is not defined
console.log(innerVariable); // innerVariable is not defined
blockScoped延伸到了全局环境,innerVariable延伸到了整个函数,而functionScoped的作用域只在函数内,外部访问不到,所以innerVariable和functionScoped都是局部变量
变量提升
当使用var声明变量时,JavaScript 引擎会在代码执行之前先处理所有的变量声明,这被称为“变量提升”。这意味着无论你在函数或全局作用域的哪个位置声明了变量,它都会被视为已经被声明在该作用域的顶部。然而,只有声明会被提升,并且被初始化为undefined,但是赋值不会被提升。
console.log(x); // 输出: undefined
var x = 10;
function exampleFunction() {
console.log(y); // 输出: undefined
var y = 20;
}
exampleFunction();
在这个例子中,尽管
x是在console.log之后声明的,但是由于变量提升,x已经在全局作用域中被声明了,因此输出的是undefined而不是抛出错误。同样的情况也发生在exampleFunction内部的y变量上。
重复声明
,var 允许在一个作用域内多次声明同一个变量名。这种行为不会导致错误,但可能会引发意外的行为,因为它实际上只是覆盖了之前的声明,并且所有这些声明都会被提升到作用域的顶部。
- 全局作用域中的重复声明
var x = 10;
console.log(x); // 输出: 10
var x = 20; // 重复声明
console.log(x); // 输出: 20
在这个例子中,x 被两次用 var 声明,第二次声明并不会报错,而是简单地覆盖了第一次声明的值。这表明即使有重复声明,也只会保留最后一次赋值。
2.函数作用域中的重复声明
function exampleFunction() {
var y = 10;
console.log(y); // 输出: 10
if (true) {
var y = 20; // 在函数内部重复声明
console.log(y); // 输出: 20
}
console.log(y); // 输出: 20
}
exampleFunction();
这里,在if语句块内再次声明了变量y。由于var具有函数作用域而不是块级作用域,因此这个新的声明实际上是覆盖了之前在同一函数内的声明,结果是整个函数体内y的值都变成了20。
3.块级作用域内的重复声明
{
var z = 10;
console.log(z); // 输出: 10
if (true) {
var z = 20; // 再次声明,但由于 var 没有块级作用域,这会覆盖外部的 z
console.log(z); // 输出: 20
}
console.log(z); // 输出: 20
}
在这个例子中,尽管z是在if语句块内重新声明的,但是因为var没有块级作用域的概念,所以它实际上覆盖了外部的z,最终整个代码块内的z都指向20。
注意:不同作用域之间不存在重复声明
if (true) {
var name = "John";
}
function sayHello() {
var name = "world";
console.log("Hello " + name + "!"); // Output: Hello world!
}
sayHello();
console.log(name) // Output: John
在这个例子中,
var name = "John";和var name = "world";不是重复声明,因为它们分别位于不同的作用域中——一个在全局作用域,另一个在sayHello函数的作用域。每个作用域内的name变量都是独立的,互不影响。
let 关键字
ES6 引入了let关键字,它提供了块级作用域的功能。这意味着使用let声明的变量只在其所在的代码块(如if语句、for循环等)内有效。此外,虽然该声明也会被提升到块的顶部,但是对这个变量的初始化(赋值)不会被提升。
if (true) {
let y = 10;
console.log(y); // 10
}
console.log(y); // ReferenceError: y is not defined.
在声明之前访问这些变量会导致引用错误(ReferenceError),这是因为它们存在一个“暂时性死区”(Temporal Dead Zone, TDZ)。TDZ是指从块的开始到变量声明的那一行之间的区域,在此期间尝试访问变量是非法的。
// 尝试在 let 声明之前访问变量会抛出 ReferenceError
console.log(a); // 抛出 ReferenceError: Cannot access 'a' before initialization
let a = 5;
const 关键字
const用于声明常量,即一旦赋值就不能再更改其绑定的值。不过需要注意的是,对于对象和数组这样的复杂数据类型,虽然不能改变它们的引用,但可以修改其属性或元素。
const person = { name: 'Alice' };
person.name = 'Bob'; // 这是允许的
person = {}; // TypeError: Assignment to constant variable.
const同样具有块级作用域。这里我们看到,当尝试在块外部访问alsoBlockScoped时,也会得到一个错误。
// 块级作用域内的 const 变量
{
const alsoBlockScoped = "So am I!";
console.log(alsoBlockScoped); // 输出: So am I!
}
console.log(alsoBlockScoped); // ReferenceError: alsoBlockScoped is not defined
const也存在暂时性死区,与let类似,留给小伙伴自行体会
变量的作用域
- 全局作用域:当变量在任何函数外部声明时,它就处于全局作用域,可以在程序的任何地方访问。
- 局部作用域:
- 函数作用域:由
var创建的变量存在于定义它们的函数内部及其嵌套函数中。 - 块级作用域:
let和const创建的变量仅限于它们被声明的块(例如if语句、for循环)内部。
- 函数作用域:由
总结
ES6 的let和const带来了更为严格和直观的作用域规则,有助于避免一些常见的编程错误。同时,它们的存在鼓励开发者采用更清晰的编码实践,比如优先使用const来声明那些不应该改变的值,从而提高代码的质量和维护性。
本文只是对let、var和const进行了一个简要的介绍。如果有不对的地方欢迎指正。