Skip to content

ES6 - 变量

变量是内存中一个命名的存储位置,用于存放值。在 JavaScript 中,变量名(或标识符)必须遵循以下规则:

  • 不能是保留关键字(例如,if、for、function)。
  • 可以包含字母、数字、下划线(_)和美元符号($)。
  • 不能以数字开头。
  • 区分大小写(myVariable 和 MyVariable 是不同的)。

在 ES6 之前,变量使用 var 关键字声明。ES6 引入了 let 和 const,用于更健壮的变量声明和作用域管理。

使用 var 声明的变量具有函数作用域或全局作用域,它们不具有块级作用域。var 声明会被提升(参见变量提升部分)。

var globalVar = "I'm global";
function exampleFunctionVar() {
var functionScopedVar = "I'm function-scoped";
console.log(functionScopedVar);
if (true) {
var blockVar = "I'm declared in a block, but still function-scoped with var";
console.log(blockVar);
}
console.log(blockVar); // 由于函数作用域,这里可访问
}
exampleFunctionVar();
// console.log(functionScopedVar); // 错误:functionScopedVar 在全局未定义

使用 let 声明的变量是块级作用域(block-scoped)。这意味着它们只能在其定义的块(例如,if 语句中的 {...}、for 循环或独立的块)内访问。let 声明会被提升(hoisted),但不会被初始化,这导致了暂时性死区(Temporal Dead Zone - TDZ)。

let globalLet = "I'm global (using let)";
function exampleFunctionLet() {
let functionScopedLet = "I'm function-scoped (using let)";
console.log(functionScopedLet);
if (true) {
let blockScopedLet = "I'm truly block-scoped!";
console.log(blockScopedLet); // 可访问
}
// console.log(blockScopedLet); // 错误:blockScopedLet 在这里未定义
}
exampleFunctionLet();

使用 const 声明的变量也具有块级作用域,类似于 let。然而,const 声明必须在声明时初始化,并且其值不能被重新赋值。这意味着变量标识符不能指向一个新的值。

重要提示:const 并不会使值本身不可变(immutable)。如果 const 变量持有的是一个对象(object)或数组(array),则该对象的属性或数组的元素仍然可以被修改。

const PI = 3.14159;
// PI = 3.14; // 错误:赋值给常量变量。
const person = {
name: 'Alice',
age: 30
};
person.age = 31; // 这是允许的!修改对象的属性。
console.log(person.age); // 31
// person = { name: 'Bob', age: 40 }; // 错误:不能重新赋值 const 变量 'person'。
const numbers = [1, 2, 3];
numbers.push(4); // 允许
console.log(numbers); // [1, 2, 3, 4]
// numbers = [5, 6]; // 错误

初始化是将一个初始值赋给变量的过程。它可以在声明时完成,也可以稍后完成(对于 var 和 let)。const 要求在声明时必须进行初始化。

let name; // 声明
name = "Alice"; // 初始化
let city = "London"; // 声明并初始化
const country = "UK"; // 声明并初始化 (const 必须同时进行)

JavaScript 是一种动态类型(dynamically-typed)语言。这意味着你在声明变量时不必指定其数据类型,并且变量的类型可以在程序执行期间改变。

let myVar = 42; // myVar 是一个数字
console.log(typeof myVar); // "number"
myVar = "Hello, world!"; // myVar 现在是一个字符串
console.log(typeof myVar); // "string"
myVar = true; // myVar 现在是一个布尔值
console.log(typeof myVar); // "boolean"

作用域(Scope)决定了变量的可访问性(可见性)。JavaScript 有几种类型的作用域:

  • 全局作用域(Global Scope): 在任何函数或块之外声明的变量具有全局作用域。它们可以在 JavaScript 代码的任何地方访问。在浏览器中,用 var 声明的全局变量会成为 window 对象的属性。用 let 和 const 在全局声明的变量则不会。
  • 函数作用域(Function Scope / Local Scope): 使用 var、let 或 const 在函数内部声明的变量仅在该函数内部可见,不能从外部访问。
  • 块级作用域(Block Scope): 使用 let 或 const 在块(例如,if、for 语句中的 {},或仅一个独立的块)内部声明的变量仅在该块内部可访问。

示例: 全局作用域、局部作用域与块级作用域

Section titled “示例: 全局作用域、局部作用域与块级作用域”
const globalNum = 10;
function checkScope() {
const functionNum = 100;
console.log('Inside function - globalNum:', globalNum); // 10
console.log('Inside function - functionNum:', functionNum); // 100
if (true) {
const blockNum = 200;
console.log('Inside block - globalNum:', globalNum); // 10
console.log('Inside block - functionNum:', functionNum); // 100
console.log('Inside block - blockNum:', blockNum); // 200
}
// console.log(blockNum); // 错误:blockNum 在这里未定义 (在其块外部)
}
console.log('Outside function - globalNum:', globalNum); // 10
// console.log(functionNum); // 错误:functionNum 在这里未定义 (在其函数外部)
checkScope();

变量遮蔽(Shadowing):如果你在某个作用域(例如,函数或块)内声明了一个与外部作用域中变量同名的变量,则内部变量在其作用域内会“遮蔽”外部变量。

变量提升(Hoisting)是 JavaScript 的默认行为,它会将声明(declarations)移动到其各自作用域(全局、函数或块)的顶部,然后再执行代码。但是,只有声明会被提升,初始化(initializations)不会。

使用 var 声明的变量会被提升到其函数作用域或全局作用域的顶部,并且会被初始化为 undefined。

console.log(hoistedVar); // undefined (声明被提升,初始化为 undefined)
var hoistedVar = "I am hoisted!";
console.log(hoistedVar); // "I am hoisted!"
// 在内部,上面代码的处理方式类似于:
// var hoistedVar; // 声明被提升并初始化为 undefined
// console.log(hoistedVar);
// hoistedVar = "I am hoisted!";
// console.log(hoistedVar);

let 和 const 的变量提升 (暂时性死区 - TDZ):

Section titled “let 和 const 的变量提升 (暂时性死区 - TDZ):”

使用 let 和 const 声明的变量也会被提升到其块级作用域的顶部,但它们不会被初始化。在声明之前(定义变量的那一行代码)访问 let 或 const 变量会导致 ReferenceError。从块的开始到遇到声明之间的这段时间被称为该变量的暂时性死区(Temporal Dead Zone - TDZ)。

// console.log(tdzLet); // ReferenceError: Cannot access 'tdzLet' before initialization (TDZ)
let tdzLet = "In TDZ until here";
console.log(tdzLet); // "In TDZ until here"
// console.log(tdzConst); // ReferenceError: Cannot access 'tdzConst' before initialization (TDZ)
const tdzConst = "Also in TDZ";
console.log(tdzConst); // "Also in TDZ"
  • 默认情况下,优先使用 const 声明值不会被重新赋值的变量。
  • 对于需要重新赋值的变量,使用 let。
  • 在现代 JavaScript 中,避免使用 var,以防止其函数作用域和变量提升行为带来的问题。
  • 为了提高可读性,即使存在变量提升,也最好在变量所属作用域(函数或块)的顶部声明变量。