ES6 let 和 const

一、基本概念与声明方式

1. let 声明

let 允许你声明一个块级作用域的局部变量,可以选择性地将其初始化为一个值。

javascript 复制代码
let x = 1;
if (true) {
  let x = 2;  // 不同的变量
  console.log(x);  // 2
}
console.log(x);  // 1

2. const 声明

const 声明创建一个块级作用域的常量,其值不能被重新赋值。

javascript 复制代码
const PI = 3.14159;
// PI = 3.14;  // TypeError: Assignment to constant variable

const obj = { name: 'John' };
obj.name = 'Jane';  // 合法,修改对象属性
// obj = {};  // 非法,不能重新赋值

二、关键特性详解

1. 块级作用域 (Block Scope)

letconst 引入了真正的块级作用域,不同于 var 的函数作用域。

javascript 复制代码
{
  let blockScoped = 'visible inside block';
  const alsoBlockScoped = 'same here';
}
console.log(blockScoped);  // ReferenceError
console.log(alsoBlockScoped);  // ReferenceError

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);  // 0, 1, 2
}

2. 暂时性死区 (Temporal Dead Zone, TDZ)

在声明前访问 letconst 变量会导致 ReferenceError,从进入作用域到声明完成之间的区域称为TDZ。

javascript 复制代码
console.log(aLetVar);  // ReferenceError
let aLetVar = 10;

// 对比var
console.log(aVar);  // undefined
var aVar = 10;

3. 无作用域提升 (No Hoisting)

虽然 letconst 声明在编译阶段被处理(类似"提升"),但在声明前不可访问。

javascript 复制代码
function test() {
  console.log(hoistedVar);  // undefined
  console.log(notHoistedLet);  // ReferenceError
  
  var hoistedVar = 1;
  let notHoistedLet = 2;
}

4. 禁止重复声明

在同一作用域内,letconst 不允许重复声明变量。

javascript 复制代码
let x = 1;
let x = 2;  // SyntaxError: Identifier 'x' has already been declared

var y = 1;
let y = 2;  // 同样报错

三、与 var 的详细对比

特性 var let const
作用域 函数作用域 块级作用域 块级作用域
提升 声明提升,初始化为undefined 存在TDZ,不可提前访问 存在TDZ,不可提前访问
全局声明时成为window属性
重复声明 允许 不允许 不允许
值可变性 可变 可变 不可重新赋值(对象内容可修改)

四、词法作用域 (Lexical Scope)

letconst 遵循词法作用域规则,作用域在代码编写时就已经确定。

javascript 复制代码
function outer() {
  let outerVar = 'outer';
  
  function inner() {
    console.log(outerVar);  // 可以访问外部变量
    let innerVar = 'inner';
  }
  
  inner();
  // console.log(innerVar);  // ReferenceError
}
outer();

五、实际应用场景

1. 循环中的变量绑定

javascript 复制代码
// var 的问题
for (var i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 0);  // 5,5,5,5,5
}

// let 的解决方案
for (let j = 0; j < 5; j++) {
  setTimeout(() => console.log(j), 0);  // 0,1,2,3,4
}

2. 常量声明

javascript 复制代码
// 配置常量
const API_ENDPOINT = 'https://api.example.com';
const MAX_RETRIES = 3;
const DEFAULT_TIMEOUT = 5000;

// 对象常量(内容可修改)
const USER_ROLES = {
  ADMIN: 'admin',
  USER: 'user',
  GUEST: 'guest'
};

3. 块级作用域实用案例

javascript 复制代码
// 条件性变量声明
if (userLoggedIn) {
  let authToken = getToken();
  // 仅在此块中使用token
}

// switch语句中的块作用域
switch (condition) {
  case 1: {
    let message = 'Case 1';
    console.log(message);
    break;
  }
  case 2: {
    let message = 'Case 2';  // 不冲突
    console.log(message);
    break;
  }
}

六、常见误区与最佳实践

1. const 并不意味着不可变

javascript 复制代码
const arr = [1, 2, 3];
arr.push(4);  // 合法
// arr = [1,2,3,4];  // 非法

const obj = { name: 'John' };
obj.name = 'Jane';  // 合法

如果需要完全不可变的对象,可以使用 Object.freeze()

javascript 复制代码
const frozenObj = Object.freeze({ name: 'John' });
frozenObj.name = 'Jane';  // 静默失败(严格模式下报错)

2. 优先使用 const,其次是 let

现代 JavaScript 开发推荐:

  1. 默认使用 const
  2. 只有当变量需要重新赋值时才使用 let
  3. 避免使用 var

3. 全局声明的影响

javascript 复制代码
var globalVar = 1;
let globalLet = 2;

console.log(window.globalVar);  // 1
console.log(window.globalLet);  // undefined

七、底层原理

1. 变量创建的三阶段

  1. 声明阶段:在作用域中注册变量
  2. 初始化阶段:分配内存并绑定作用域(TDZ结束)
  3. 赋值阶段:给变量赋值

var 在声明阶段即初始化(为undefined),而 let/const 将初始化和赋值分开。

2. 执行上下文的变化

javascript 复制代码
{
  // let x 的声明被"提升"但未初始化
  console.log(x);  // ReferenceError
  let x = 10;
  // 初始化完成
}

八、迁移建议

var 迁移到 let/const 时:

  1. 将函数内所有 var 改为 const
  2. 将需要重新赋值的变量改为 let
  3. 注意检查循环和闭包中的变量引用
  4. 使用 linter 工具帮助检测 var 的使用
javascript 复制代码
// 迁移前
function oldWay() {
  var a = 1;
  var b = 2;
  for (var i = 0; i < 10; i++) {
    // ...
  }
}

// 迁移后
function newWay() {
  const a = 1;
  let b = 2;
  for (let i = 0; i < 10; i++) {
    // ...
  }
}
相关推荐
前端小L4 小时前
双指针专题(三):去重的艺术——「三数之和」
javascript·算法·双指针与滑动窗口
0和1的舞者4 小时前
Spring AOP详解(一)
java·开发语言·前端·spring·aop·面向切面
web小白成长日记4 小时前
在Vue样式中使用JavaScript 变量(CSS 变量注入)
前端·javascript·css·vue.js
QT 小鲜肉4 小时前
【Linux命令大全】001.文件管理之which命令(实操篇)
linux·运维·服务器·前端·chrome·笔记
C_心欲无痕4 小时前
react - useImperativeHandle让子组件“暴露方法”给父组件调用
前端·javascript·react.js
霖鸣6 小时前
Minecraft通过kubejs进行简单魔改
javascript
JackieDYH6 小时前
HTML+CSS+JavaScript实现图像对比滑块demo
javascript·css·html
BullSmall6 小时前
支持离线配置修改及删除操作的实现方案
前端
全栈前端老曹7 小时前
【前端路由】Vue Router 嵌套路由 - 配置父子级路由、命名视图、动态路径匹配
前端·javascript·vue.js·node.js·ecmascript·vue-router·前端路由
EndingCoder7 小时前
安装和设置 TypeScript 开发环境
前端·javascript·typescript