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

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

用 const 和 let 导出的变量在实时绑定上并无本质区别,二者都建立实时绑定,导入端始终看到最新值;差异仅在导出模块内是否允许重新绑定。

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

导出绑定与只读视图

  1. 实时绑定导出变量与导入方共享同一存储绑定位置
  2. 绑定可变性const 不可重绑定,let 可,但仅限导出侧
  3. 导入端只读不能对导入变量赋值,但可修改其属性

实时绑定不区分 const/let,差异只在导出侧是否允许重新赋值。

核心回答

先记住这个答案

ESM 中导出声明会为变量创建实时绑定,无论 const 或 let,导入方读取的都是绑定对应的当前值。const 仅禁止导出模块内重新赋值,let 允许,但两者在导入端均为只读,无法对导入变量赋值。若导出的是对象,const 导出仍可通过修改对象属性让导入端看到变化。因此选择 let 还是 const 主要取决于导出模块是否需要重新绑定变量,实时绑定本身不受影响。

  • 实时绑定对 const 与 let 均成立,无差异
  • 差异仅在导出模块内能否重新绑定
  • 导入端始终只读,不可重新赋值

同一绑定的两种声明

ESM 的导出并非拷贝值,而是注册一个绑定。当模块顶层执行 let count = 0 后 export { count },导入方读取 count 时访问的是该模块作用域内的名字。若后续模块内执行 count = 1,导入方再次读取会得到 1。const 声明的绑定同样被实时跟踪,只是 count 被禁止重新赋值。所以实时绑定与关键字无关,它是模块解析器为每个导出名称建立的一个活引用。

导入方得到的标识符在词法上等价于一个 const,无论原导出是 const 还是 let。因此尝试对导入变量赋值会触发 TypeError,因为模块命名空间对象的属性不可写。但若导出的是对象,导入方可修改其属性,因为那改变的是对象内容而非绑定。设计上,绑定所有权唯一归属导出模块,外部只能观察或间接操作。

计数器模块与配置常量

假设一个在线状态模块需要将当前连接数暴露给多个组件,连接数在建立/断开时变化。用 let 导出该计数,并在内部通过函数更新。每次调用更新后,导入方立即看到新值,因为实时绑定指向同一个 count。若改为 const 导出,模块内部无法重新赋值,只能改用对象 { value: 0 } 并修改属性,这会让调用方必须访问 state.value,增加额外层级且可能无意中改写整个对象。

选择 let 导出原始数字可避免包裹对象,且模块内所有赋值操作都经过受控函数,外部无法直接改写。但需警惕 let 允许模块内任何位置重新绑定,多人协作时可能被误改。若业务要求数值本身不变,只是通过增减派生新值,则应该用 const 导出不可变数据(如状态快照),用新变量保存每次变化。具体应由数据是否需要原地更新来决定。

失效条件与转换代价

实时绑定在模块链接完成后即生效,但若构建工具(如 webpack)将模块打包成非原生 ESM 并启用 namespace 重导出,一些打包配置可能生成快照拷贝而非实时绑定,导致 let 更新不可见。另外,动态 import() 返回的命名空间对象并非冻结后属性值就不再变化——值仍实时更新。但若导入方缓存在函数外部读取的变量,则不会自动更新,需要每次通过命名空间读取。

另一个边界:如果导出的是 let 但模块在初始化后从不执行赋值语句,实时绑定效果与静态值无异;若涉及循环依赖,let 绑定在初始化阶段可能还未完成赋值,导入方在顶层读取可能拿到 undefined。本题不深入循环依赖,但选择 const 导出对象时也要防范循环引用中对象方法提前调用。处理此类情况需推迟读取时机(如放入函数体内),并确保导出前完成必要赋值,代价是增加代码顺序约束和测试复杂度。

回答前,多想一步

容易答错的地方

const 导出不是实时绑定
错误认为 const 导出时导入方看到初始化值后不再变化。事实上 const 只是禁止导出模块重新绑定,但若导出对象并修改其属性,导入方仍能看到新值,实时绑定机制本身与 const 无关。
导入 let 变量可以重新赋值
有人以为导入的 let 像普通变量那样可改,实际导入标识符是只读视图,无论原导出是 let 还是 const,赋值都会抛出 TypeError。需要修改只能修改对象属性或由导出模块提供 setter。
试着用自己的话回答

面试官还会怎么问?

导出 const 对象时修改属性,导入方是否实时看到?

会。实时绑定跟踪的是绑定而非值。对象属性修改发生在对象内部,不改变绑定本身,因此导入方通过同一引用能看到变化。这也适用于数组的 push 等方法。

如果要导出一个可变的数字,用 let 还是 const?

应该用 let,因为数字是原始值,修改绑定是唯一更新途径。若用 const 则无法重新赋值,只能换成对象包裹或改用其他设计。let 导出在实时绑定下表现正常。

为什么 import 的变量不能赋值,但导出的 let 可以?

模块设计将绑定所有权限定在导出模块内。导入端获得的是只读视图,防止外部模块直接变更他人状态,从而维护封装。若需变更,必须通过导出方提供的函数或对象方法间接操作。

从一道题,走向一组知识

把知识连起来

模块与语言机制

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

同属「模块与语言机制」专题,接着看 ESM 实时绑定与 CommonJS 区别 在具体场景中的处理方式。

模块与语言机制

import * as ns 与具名导入在实时绑定和 Tree-shaking 上有什么不同表现?

同属「模块与语言机制」专题,接着看 import * as 与具名导入 tree-shaking 区别 在具体场景中的处理方式。

模块与语言机制

ESM 中具名导出与默认导出的主要区别是什么,什么时候该用默认导出?

同属「模块与语言机制」专题,接着看 ESM named export vs default export 在具体场景中的处理方式。

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

参考资料

  • JavaScript modules

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

本题目录
  1. 先记住这个答案
  2. 同一绑定的两种声明
  3. 计数器模块与配置常量
  4. 失效条件与转换代价
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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