前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库ESM export const 与 let 实时绑定区别
JavaScript模块与语言机制

用 const 和 let 导出的变量,在实时绑定上有什么区别?

区别在于源模块能不能重新赋值。export const 的变量不能被重新赋值,导入方读到的永远是初始值——不过如果导出的是对象,对象的属性还是可以修改的。export let 的变量可以在源模块里重新赋值,导入方通过实时绑定随时读到最新值。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#模块与语言机制
先看核心答案读代码示例
理解线索

`const` 和 `let` 导出的差异

  1. export const源模块不能重新赋值,导入方始终读到初始值
  2. export let源模块可以重新赋值,导入方通过实时绑定读到最新值
  3. 导入方的限制不管源端是 const 还是 let,导入方都不能给绑定赋值

实际项目里大多数导出都用 const。只有确实需要模块间共享可变状态(比如计数器、配置项热更新)才用 let 导出。

核心回答

先记住这个答案

export const 和 export let 都会建立实时绑定,导入方读到的始终是源模块变量的当前值。区别在于 const 声明的变量不能被重新赋值,所以它的值在初始化之后就不会变了——导入方每次读到的都一样。let 声明的变量可以在源模块内部被重新赋值,每次赋值后导入方立刻就能读到新值。

  • const 导出后源模块不能重新赋值,值不会变
  • let 导出后源模块可以改,导入方实时看到新值
  • 不管用哪种声明,导入方都不能赋值
  • const 导出对象时,对象属性仍然可以被修改

用代码看清区别

两个模块分别用 const 和 let 导出一个计数器,然后在源模块内部尝试修改。

const 和 let 导出的不同行为JavaScript
// config.mjs
export const VERSION = '1.0'
export let count = 0

export function increment() {
  // VERSION = '2.0'  // 报 TypeError,const 不能重新赋值
  count++             // 正常,let 可以重新赋值
}

// main.mjs
// import { VERSION, count, increment } from './config.mjs'
// console.log(count)    // 0
// increment()
// console.log(count)    // 1 —— 实时绑定,读到了新值
// console.log(VERSION)  // '1.0' —— const 不会变

count 用 let 声明,源模块调用 increment() 后 count 变成了 1,导入方立刻就能读到。VERSION 用 const 声明,源模块自己都改不了,导入方自然也永远看到 '1.0'。

为什么大多数导出都用 const

实际项目里,绝大多数导出用 const 就够了。函数声明和类声明导出后行为和 const 一样——你不能在源模块里把导出的函数名重新赋值给另一个函数。

用 let 导出意味着允许源模块在运行过程中改变这个变量的值,其他所有导入方都会同步感知到。这是一种跨模块的共享可变状态,用得多了代码会很难追踪——你不知道 count 什么时候被谁改了。所以只有确实需要模块间共享状态的场景才用 let 导出。

比较典型的场景有两个。一是热更新配置:配置模块用 let 导出当前配置对象,接收到新配置时重新赋值,所有导入方立刻生效。二是状态管理的底层实现:一些轻量状态管理库利用 ESM 实时绑定来通知视图层更新。

const 导出对象时的陷阱

const 只是阻止变量被重新赋值,不阻止对象内部的属性被修改。这是 const 本身的语义,和模块系统无关。

如果 export const config = { debug: false },源模块可以写 config.debug = true,导入方也会读到 true。这时候虽然 config 这个绑定没变(还是指向同一个对象),但对象的内容变了,导入方通过实时绑定读到的还是同一个对象,自然能看到属性的变化。

想让对象完全不可修改,可以用 Object.freeze()。export const config = Object.freeze({ debug: false }) 之后,任何对 config.debug 的修改都会被静默忽略(严格模式下报 TypeError)。

回答前,多想一步

容易答错的地方

以为 const 导出就没有实时绑定
实时绑定是 ESM 的底层机制,和 const 还是 let 无关。const 导出也是实时绑定——只是因为值不会变,实时绑定在这里"没有意义"而已。绑定本身是存在的,只是永远返回同一个值。
以为导入方可以给 let 导出的变量赋值
不管源端用 const 还是 let,导入方的绑定都是只读的。import { count } 之后,导入方写 count = 10 会报 TypeError。只有源模块自己能改变 let 导出的值。
试着用自己的话回答

面试官还会怎么问?

export function 导出的函数和 export const fn = () => {} 有区别吗?

在实时绑定上没有区别,都不能被重新赋值。export function foo() 有函数提升,在模块内 foo 可以在声明之前使用。export const fn = () => {} 没有提升,在声明之前使用会报 ReferenceError。在导入方看来两者行为完全一样。

如果源模块用 setInterval 每秒修改 let 导出的变量,导入方能实时感知吗?

能。let 导出的变量每次被修改后,导入方下一次读取时就能看到新值。但"实时绑定"不意味着有推送机制——导入方不会收到通知,它只是在下次读取时拿到当前值。如果需要主动通知,还是要用发布订阅模式或者响应式库。

var 导出会怎样?

ESM 里 export var count = 0 也是合法的,行为和 export let 类似——var 可以被重新赋值,导入方能通过实时绑定看到新值。但现代代码很少用 var,因为 var 的函数作用域和变量提升容易引发其他问题。

从一道题,走向一组知识

把知识连起来

模块与语言机制

为什么 ESM 导出的变量是实时绑定,CommonJS 不是?

这道题比较 ESM 实时绑定和 CommonJS 值拷贝的差异,是理解实时绑定的前置知识。

模块与语言机制

import * as ns 和具名导入在 tree-shaking 上有什么区别?

import * as ns 拿到的命名空间对象上的属性也是实时绑定的,受源端 const 还是 let 的影响。

模块与语言机制

具名导出和默认导出有什么区别?什么时候该用哪种?

默认导出本质上是一个叫 default 的具名导出,它的实时绑定行为和声明方式有关。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • JavaScript modules

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 用代码看清区别
  3. 为什么大多数导出都用 const
  4. const 导出对象时的陷阱
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑