Front-End-Checklist 前端清单:用 const 与 let 取代 var 的块级作用域实战指南
【免费下载链接】Front-End-Checklist🗂 The essential checklist for modern web development, for humans and AI agents项目地址: https://gitcode.com/gh_mirrors/fr/Front-End-Checklist
本文以 Front-End-Checklist 仓库中的 const-let 规则文档 为主体,讲解为什么在现代 JavaScript 中应彻底放弃
var,改用块级作用域的const与let。你将掌握函数作用域与提升(hoisting)的真实危害、暂时性死区(TDZ)的行为差异、const与let的选择标准,以及如何在 ESLint、Biome 与仓库内置的 AI 审查工具中自动执行这一规则。
规则概览:一条高优先级、面向初学者的 JavaScript 基础规则
在 Front-End-Checklist 的规则体系中,const-let 规则被标记为priority: high(高优先级)、difficulty: beginner(初学者难度)、estimatedTime: 10 分钟,归属 JavaScript 分类下的 variables(变量)子类,规则本体记录在 packages/content/rules/en/javascript/const-let.mdx。
其核心主张可以浓缩为一句话:使用块级作用域的const和let声明,替代函数作用域的var,以避免提升引发的 bug 和意外的变量变更。ES2015 引入的块级作用域声明,解决的正是var的函数作用域与提升行为带来的真实问题。
为什么 var 会出问题:函数作用域与提升
var的作用域是函数级的,而非块级。这意味着在if、for、while等代码块内部用var声明的变量,会"泄漏"到整个函数体中,可被块外代码访问。更隐蔽的是提升(hoisting):var声明会被提升到函数顶部,声明被提升而赋值停留在原地,导致变量在声明之前就能被访问(值为undefined而非抛出错误)。
规则文档用两个经典例子揭示了这两种危害:
// ❌ 错误:var 泄漏出代码块 for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100) } // 输出:3, 3, 3 —— 而不是 0, 1, 2! // ✅ 正确:let 是块级作用域 for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100) } // 输出:0, 1, 2第一个例子中,三个闭包共享同一个函数作用域下的i,循环结束后i已是 3,因此所有回调都打印 3。改用let后,每次迭代都会创建一个新的块级绑定,闭包捕获各自的i,行为符合直觉。
// ❌ 错误:var 被提升,声明前即可访问 console.log(name) // undefined,而不是 ReferenceError var name = 'Alice' // ✅ 正确:let/const 在暂时性死区中抛出 ReferenceError console.log(name) // ReferenceError: Cannot access 'name' before initialization let name = 'Alice'第二个例子展示了暂时性死区(Temporal Dead Zone, TDZ)的价值:let/const同样会被提升,但在"声明初始化完成之前"的这段区域内访问变量,会直接抛出ReferenceError,将潜在 bug 在运行的第一时间暴露出来,而不是静默得到undefined。
何时用 const、何时用 let
规则的 Quick Reference 给出了明确的决策标准:
- 用
const:绑定(binding)永远不会被重新赋值; - 用
let:后续需要重新赋值; - 绝不用
var:函数作用域 + 提升,易引发隐蔽 bug; const不等于不可变:用const声明的对象与数组仍可被修改。
对应的代码示例:
// ✅ 绑定不会被重新赋值时使用 const const MAX_RETRIES = 3 const apiUrl = 'https://api.example.com' const user = fetchUser() // 恒定的是绑定本身,而不是对象 // ✅ 需要重新赋值时使用 let let count = 0 count++ let status = 'pending' status = 'complete'const的核心价值在于表达意图:看到const,读者立即知道这个绑定不会再指向其他值,从而更容易理解数据流。这也是规则文档强调"const signals that a binding should not be reassigned, which helps readers understand data flow"的原因——声明方式本身就是一种自文档化的代码注释。
const 不等于 immutable:对象与数组的边界
初学者最常见的误区是把const当成"深冻结"。规则文档特别用一整节澄清这一点:
// ✅ const 只阻止对绑定的重新赋值 const user = { name: 'Alice', role: 'admin' } user.role = 'viewer' // ✅ 允许修改属性 user = { name: 'Bob' } // ❌ TypeError: assignment to constant variable // 想要真正不可变的对象,请使用 Object.freeze const config = Object.freeze({ debug: false, version: '1.0' }) config.debug = true // 松散模式下静默失败,严格模式下抛出异常const保证的是引用(reference)不变,而不是内容不变。若需要真正的不可变对象,应使用Object.freeze(浅冻结)或结合展开运算符(spread)创建新对象。这条规则在仓库中被进一步关联到了 immutable-patterns(不可变模式)——正如 const-let 规则的元数据中所述:const只是第一步,不可变模式通过Object.freeze和 spread 走得更远。
用 ESLint 自动强制:no-var 与 prefer-const
人工审查难免遗漏,规则文档推荐用 ESLint 将这一约定固化为强制检查:
{ "rules": { "no-var": "error", "prefer-const": "error" } }no-var:任何var声明都会被标记为错误,直接杜绝其进入代码库;prefer-const:当变量从未被重新赋值时,强制改用const。
仓库中的 javascript-linter 规则 给出了一个更完整的生产级配置示例:在eslint:recommended与airbnb-base基础上,将prefer-const: 'error'与no-var: 'error'与no-eval、no-implied-eval、no-new-func等错误预防规则一同开启,配合parserOptions.ecmaVersion: 'latest'与sourceType: 'module',确保 ES2015+ 语法能被正确解析。
值得注意的是,本仓库自身的 lint 工具链基于Biome:根目录 package.json 定义了lint(biome lint .)、lint:fix(biome lint --write .)与format(biome format --write .)脚本,各子包(如 apps/web/package.json)统一使用biome check .。在 biome.json 中,与本文主题相关的规则是style.useConst(当前显式关闭)。若你的项目切换到 Biome,对应替代规则为useConst与noVar,可以将它们提升为"error"以获得等效的强制效果。
自动化落地:仓库内置的 AI 代码审查检测
本仓库不仅提供文档,还提供了真正可运行的检测实现。在 MCP 工具的代码审查功能 packages/mcp/src/tools/review-code.ts 中,const-let 规则被实现为一段正则检测逻辑:
// const-let — var is function-scoped and hoisted, leading to subtle bugs if (slug.includes('const-let') || slug === 'var-usage') { const varUsage = (code.match(/\bvar\s+/g) || []).length if (varUsage > 0) { return { hasIssue: true, issue: `Found ${varUsage} var declaration(s) — use const (preferred) or let instead` } } }该实现通过/\bvar\s+/g统计源码中var关键字出现的次数,一旦大于 0 即报告问题,并给出建议文案"use const (preferred) or let instead",与规则文档的修复指引完全一致。对应的单元测试位于 packages/mcp/tests/unit/review-code-detection.test.ts:
it('detects var declarations', () => { const js = 'var x = 1; var y = 2;' const rules = rulesDetectedIn(js, ['javascript']) expect(rules).toContain('const-let') })测试验证了检测器能从var x = 1; var y = 2;中识别出两条var声明并触发 const-let 规则。这意味着无论你是用 ESLint/Biome 做静态检查,还是用仓库提供的 MCP 审查能力做 AI 辅助审查,这一规则都能被自动执行——文档、实现、测试三者在仓库中形成闭环。
修复策略与验证方法
按照 SKILL.md 定义的执行流程,处理一个var违规的完整路径是:
- Check(检查):扫描 JavaScript 文件中的每一处
var,记录行号; - Fix(修复):将
var替换为const或let——从未重新赋值用const,需要重新赋值用let; - Explain(解释):说明为何
const/let优于var,覆盖作用域、提升与暂时性死区三个概念; - Code Review(代码审查):审查脚本、客户端组件与浏览器执行路径,标记出违反规则的精确导入语句、事件处理器、运行时副作用或阻塞操作,并说明修改后如何在浏览器中验证。
规则文档 references/rule.md 进一步给出了验证清单:
- 自动化检查:代码变更后要在浏览器中验证实际行为,而不是只看静态分析结果;当规则影响加载或执行顺序时,检查 DevTools 的 Network 或 Performance 面板;测试主要用户流程与变更脚本路径触发的一个边界用例;
- 手动检查:确认功能在延迟、懒加载或失败场景下依然行为正确。
小结
const/let替代var是 ES2015 之后最基础、收益最直接的编码约定之一:它消除了闭包捕获共享变量这类经典陷阱,用暂时性死区把初始化顺序问题显性化,并用声明关键字本身传达了数据流的意图。在 Front-End-Checklist 项目中,这一规则同时以三种形态存在——面向人类阅读的 规则文档 与 Skill 参考文档、面向 Agent 的 SKILL.md(内含 check/fix/explain/codeReview 提示词),以及可自动执行的 MCP 检测实现 与 单元测试。配合 ESLint 的no-var/prefer-const或 Biome 的useConst/noVar,你的代码库可以在几分钟内彻底告别var。
【免费下载链接】Front-End-Checklist🗂 The essential checklist for modern web development, for humans and AI agents项目地址: https://gitcode.com/gh_mirrors/fr/Front-End-Checklist
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考