关于 let 与 const
December 22, 2019
我在上一篇文章中提到了这样一段话:
let
vsconst
vsvar
:通常你会用let
。如果你想禁止对这个变量重新赋值,可以用const
。(有些代码库或同事非常较真,会强制你在只赋值一次时用const
。)
没想到这段话引发了很大争议,在 Twitter 和 Reddit 上都引起了讨论。看起来主流观点(或者说,表达最激烈的观点)是:应当尽可能使用 const
,只有在必要时才用 let
,这也可以通过 prefer-const
这样的 ESLint 规则来强制执行。
在这篇文章中,我会简要总结我遇到的一些观点和反驳,以及我个人对此的结论。
为什么要 prefer-const
- 统一做法:每次都要在
let
和const
之间做选择会增加心理负担。“能用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
语句,即使b
和c
的分支很复杂,给它们单独命名也很别扭。 - 重新赋值未必有 bug:重新赋值常见的 bug 有三种情况:作用域很大(比如模块作用域或超大函数)、变量是参数(所以不希望它变成别的值)、变量在嵌套函数中被用到。但实际上,大多数代码库里的变量都不属于这些情况,而且参数也不能被标记为常量。
- 没有性能提升:据我了解,JavaScript 引擎本来就能知道哪些变量只赋值一次——即使你用的是
var
或let
。如果我们非要猜测,也可以说多余的检查反而会带来性能损耗。但实际上,引擎已经很智能了。
我的结论
我不在乎。
我会遵循代码库里已经存在的约定。
如果你很在意,可以用 linter 自动检查和修复,这样 let
换成 const
就不会拖慢代码评审。
最后,记住 linter 是为你服务的。如果某个 linter 规则让你和你的团队觉得烦,就删掉它。也许真的没必要。多从自己的错误中学习吧。
Pay what you like