overreactedby Dan Abramov

关于 let 与 const

December 22, 2019

我在上一篇文章中提到了这样一段话:

let vs const vs var:通常你会用 let。如果你想禁止对这个变量重新赋值,可以用 const。(有些代码库或同事非常较真,会强制你在只赋值一次时用 const。)

没想到这段话引发了很大争议,在 Twitter 和 Reddit 上都引起了讨论。看起来主流观点(或者说,表达最激烈的观点)是:应当尽可能使用 const,只有在必要时才用 let,这也可以通过 prefer-const 这样的 ESLint 规则来强制执行。

在这篇文章中,我会简要总结我遇到的一些观点和反驳,以及我个人对此的结论。

为什么要 prefer-const

  • 统一做法:每次都要在 letconst 之间做选择会增加心理负担。“能用 const 就用 const”这样的规则可以让你不用再思考这个问题,而且可以通过 linter 自动检查。
  • 重新赋值可能导致 bug:在较长的函数中,变量被重新赋值很容易被忽略,这可能会导致 bug。尤其是在闭包中,const 能让你确信你看到的总是同一个值。
  • 理解变量变异:刚接触 JavaScript 的人常常误以为 const 代表不可变。但也有人认为,早点区分变量的“变异”和“赋值”很重要,而偏向使用 const 能促使你尽早理解这个区别。
  • 无意义的赋值:有些情况下,赋值本身就没有意义。例如在 React Hooks 中,从像 useState 这样的 Hook 得到的值更像是参数,是单向流动的。如果你尝试赋值而报错,可以更早地理解 React 的数据流。
  • 性能提升:偶尔有人声称,JavaScript 引擎因为知道变量不会被重新赋值,所以用 const 代码会跑得更快。

为什么不 prefer-const

  • 意图丢失:如果我们强制所有能用 const 的地方都用 const,就失去了表达“某个变量不能被重新赋值很重要”这种意图的能力。
  • 与不可变混淆:每次讨论为什么要偏向用 const 时,总会有人把它和不可变搞混。这其实不奇怪,因为赋值和变异都用 = 操作符。对此,通常的回应是“应该学好这门语言”。但反过来说,如果一个特性本来是为了防止新手犯错,却反而让新手困惑,那它其实没什么帮助。而且它也无法防止跨模块、影响所有人的“变异”类 bug。
  • 避免重新声明的压力:以 const 为首的代码风格,会让人不愿意用 let 来声明需要条件赋值的变量。例如,你可能会写成 const a = cond ? b : c,而不是用 if 语句,即使 bc 的分支很复杂,给它们单独命名也很别扭。
  • 重新赋值未必有 bug:重新赋值常见的 bug 有三种情况:作用域很大(比如模块作用域或超大函数)、变量是参数(所以不希望它变成别的值)、变量在嵌套函数中被用到。但实际上,大多数代码库里的变量都不属于这些情况,而且参数也不能被标记为常量。
  • 没有性能提升:据我了解,JavaScript 引擎本来就能知道哪些变量只赋值一次——即使你用的是 varlet。如果我们非要猜测,也可以说多余的检查反而会带来性能损耗。但实际上,引擎已经很智能了。

我的结论

我不在乎。

我会遵循代码库里已经存在的约定。

如果你很在意,可以用 linter 自动检查和修复,这样 let 换成 const 就不会拖慢代码评审。

最后,记住 linter 是为服务的。如果某个 linter 规则让你和你的团队觉得烦,就删掉它。也许真的没必要。多从自己的错误中学习吧。

Pay what you like

Edit on GitHub