JavaScript函数参数与局部变量遮蔽问题:作用域疑问求助
函数参数与函数体的作用域解析(针对case 2的异常现象)
核心结论
带参数默认值的函数,参数列表会形成独立的词法环境,作为函数体词法环境的父环境。两者并非同一作用域,但参数绑定属于函数作用域的顶层范畴,因此存在特殊的声明与遮蔽规则。
疑问1:为何无法用let变量遮蔽参数id?
函数体内尝试let id = 5时报错“标识符'id'已声明”,原因是:
- 函数参数的绑定属于整个函数作用域的顶层,无论是否设置默认值,参数名都会成为函数作用域的一部分。
- let/const遵循“同一作用域不可重复声明”规则,因此在函数体内用let声明同名变量,等同于在同一作用域重复定义,直接触发语法错误。
而for循环的场景不同:
- for循环的初始化块(
let i=0)属于独立的迭代作用域,循环体是嵌套的块级作用域。循环体内的let i=1是在子作用域中声明,属于合法的变量遮蔽,因此不会报错。
疑问2:为何var id=5未更新参数id,反而遮蔽了它?
这是var的特性与闭包捕获规则共同作用的结果:
- 闭包的捕获对象:参数
defaultID的默认值() => id,其中的id指向的是参数作用域中的绑定(即传入的3),闭包会永久保留对这个作用域的引用。 - var的变量提升与遮蔽:函数体内的
var id=5会被提升到函数体作用域顶部,但var允许重复声明的特性,会在函数体作用域中创建一个新的id绑定,遮蔽参数作用域中的原始id。 - 执行逻辑差异:
- 调用
log(id)时,访问的是函数体作用域中被赋值为5的新id,因此输出5。 - 调用
defaultID()时,闭包访问的是参数作用域中未被修改的原始id,因此输出3。
- 调用
如果将var id=5改为直接赋值id=5,则会修改参数作用域中的原始绑定,此时defaultID()会返回5,与case1的表现一致。
内容的提问来源于stack exchange,提问作者lochiwei
相关产品推荐
相关产品推荐

