JavaScript const 变量赋值错误:运行时错误与规范演变

代码笔记2周前更新
54 0

constlet 的唯一区别在于,const 声明的变量不能被重新赋值(只读变量)。例如,下面的代码会报错:

:本文不会使用“常量”这个术语,因为它容易产生歧义。例如:

  • 有些人将数字、字符串等不可改变的字面量称为常量。
  • 有些人把一些只读属性(如 Math.PI)称为常量。
  • 还有人把 ES6 中 const 声明的变量称为常量。

但一般来说,这些歧义并不会影响理解。

const 赋值错误:静态错误 vs 运行时错误

遗憾的是,为 const 变量重新赋值并不是静态错误(static error),而是运行时错误(runtime error)

  • 静态错误(static error):解析时就能检测到的错误,规范中称为提前错误(early error)
  • 运行时错误(runtime error):代码执行过程中才会抛出的错误。

为什么静态错误更好?

通常来说,错误越早发现越好。例如:

如果 foo = 2 是静态错误,开发环境就能直接报错,即使这行代码没有被执行到。

const 赋值为何不是静态错误?

早期 const 设计:静态 + 运行时错误

在 2011 年 const 刚进入 ES6 草案时,严格模式下为 const 变量重新赋值会抛出 静态错误(SyntaxError),同时在运行时也会抛出 运行时错误(TypeError)。Firefox 7 的 SpiderMonkey 也实现了这一规则。

后来,在 2012 年,草案修改为不论是否严格模式,都抛出静态错误。Firefox 36 在 2014 年 11 月 19 日实现了这一改动。

为什么 const 不能完全是静态错误?

有些情况很难静态检测,比如:

1. eval() 动态执行代码

引擎无法在解析阶段静态检测 eval() 里的 foo = 2

2. 变量声明顺序影响静态检测

在解析 foo = 2 时,foo 还未声明,引擎无法静态检测错误。

3. const 变量分散在多个 <script> 标签中

解析第二个 <script> 时,引擎可能不会去检查 foo 是否在前面声明过。

TC39 最终决策:移除 const 赋值的静态错误

在 2014 年 11 月 18 日的 TC39 会议上,委员会决定:

  • 删除 const 变量赋值的静态错误,只留下运行时错误。
  • 原因:
    • 不同引擎对“哪些情况要报静态错误”达不成共识。
    • V8 认为静态检测 const 赋值错误会导致性能问题,且难以优化。
    • 这种错误较少见,可以交给 ESLint 这样的工具来检测。

Firefox 的 SpiderMonkey 实现了静态错误后,被 TC39 要求撤回,开发者对此也表示不满。

const 在代码风格中的使用

目前推荐的代码风格是:

  • 默认使用 const,只有当变量需要重新赋值时才使用 let
  • ESLint 提供 prefer-const 规则来强制执行这种风格。
  • 但如果没有 no-const-assign 规则配合,可能会导致 const 变量被误修改而在生产环境抛出 TypeError

如何判断错误是静态错误还是运行时错误?

一般情况下:

  • SyntaxError 通常是静态错误。
  • 其他类型的错误通常是运行时错误。
JavaScript const 变量赋值错误:运行时错误与规范演变

结论

  1. const 变量不能重新赋值,但这不是静态错误,而是运行时错误。
  2. 早期 ES6 版本尝试将 const 赋值错误作为静态错误,但最终放弃。
  3. 运行时错误仍然保留,并且不受严格模式影响。
  4. 代码风格推荐:默认使用 const,只有在需要修改变量时才用 let
  5. 使用 ESLint prefer-const 规则,同时配合 no-const-assign,避免潜在 bug。
  6. 静态错误和运行时错误的区别可通过 alert() 进行简单测试。

这就是 const 赋值错误背后的故事,它不仅仅是个语法设计问题,更涉及到编译器实现、性能权衡和语言规范的演变。

© 版权声明

暂无评论

本文暂时没有评论,来添加一个吧(●'◡'●)