Google Closure Compiler对let声明报JSC_CONSTANT_REASSIGNED_VALUE_ERROR警告
先还原你的场景:
原始待压缩代码:
{ let x = 10, y = 20; console.log(y); } { let x = 30, y = 40; console.log(y); }
编译器抛出的警告:
JSC_CONSTANT_REASSIGNED_VALUE_ERROR: constant y assigned a value more than once. Original definition at Input_0:4 at line 10 character 6 y = 40; ^
输出的ES5代码:
var x=10,y=20;console.log(y);var x$0=30;y=40;console.log(y);
你的疑问逐个解答
1. 这些let声明是否存在无效之处?
完全没问题!你的代码是标准的ES6+语法,两个独立代码块里的let声明各自被块作用域隔离,语法和语义上都完全合法。
2. 声明被包裹在独立块中,变量应相互独立,是否正确?
这个理解100%正确。ES6规范明确规定let声明的变量拥有块级作用域,两个{}属于完全独立的作用域,所以两个x、两个y本应是毫无关联的独立变量。
3. 为何会出现该警告?
这是Closure Compiler在SIMPLE_OPTIMIZATIONS模式下的优化误判。编译器为了缩减代码体积,会尝试复用变量,但它错误地认为第二个块的y可以和第一个块的y复用,于是把第二个let y当成了对同一个变量的重复赋值,从而抛出了这个警告。
4. 为何警告仅针对y而非x?
这是编译器优化逻辑的细节差异。它在判断变量是否可复用时,可能基于内部规则(比如变量使用场景、名称优先级等),选择了尝试复用y而非x,并非你的代码存在差异。简单说就是编译器优化策略里的随机选择,和代码本身无关。
5. 为何将y称为“constant”?我并未声明任何常量。
这是编译器优化的“乌龙”行为。当它标记第一个块的y为可复用变量时,内部可能把它当成了“不会被重新赋值的常量”(毕竟第一个块里y只赋值了一次),但看到第二个块的y赋值时,就误以为是对这个“内部标记常量”的重新赋值,所以抛出了这个表述不准确的警告。
关于ES5输出:为何复用y但不复用x?
这同样是Closure Compiler在SIMPLE_OPTIMIZATIONS模式下的变量复用逻辑导致的:
- 对于
y,编译器判断第一个块执行完毕后,没有其他同名变量会占用这个名称(在它的优化逻辑里),所以直接复用了该变量,把第二个块的let y = 40改成了y = 40。 - 对于
x,编译器可能出于某种风险规避(比如担心第一个x有潜在后续引用)或者内部变量命名规则,选择创建新变量x$0来替代第二个块的x,而非复用第一个x。
这种优化其实违背了ES6块级作用域的语义,属于特定优化模式下的行为偏差。如果想要严格遵循ES6语义的编译结果,可以尝试切换到ADVANCED_OPTIMIZATIONS模式,或者调整编译器配置禁用这类变量复用优化。
内容的提问来源于stack exchange,提问作者Oliver

