这是一个很好的解释!是的,参数列表有它自己的范围。让我稍微扩展一下你的解释,然后解释为什么有额外的范围。
当您调用没有默认参数值的函数时,将为函数体创建一个新作用域,并在该作用域中创建参数,就像函数中的顶级变量一样。所以,从概念上来说:
<<
let param1 = /*...the argument value provided for param 1 if any */;
let param2 = /*...the argument value provided for param 2 if any */;
// Start of function code
var variable1;
let variable2;
// End of function code
>>
(我在用着<<
/>>
分隔符而不是{
/}
因为作用域不仅仅是块作用域,它们隔离var
以及;所以我选择了一个任意的分隔符。)
当存在默认参数值时,正如您所描述的,会涉及一个额外的范围:
<<
let param1 = /*...the argument value provided for param 1 if any */;
let param2 = /*...the argument value provided for param 2 if any */;
<<
// Start of function code
var variable1;
let variable2;
// End of function code
>>
>>
原因是默认参数值是表达式,而不仅仅是文字。例如:
function example(a = 1, b = a + 1) {
// ^^^^^−−−−−−−−−−−−− expression, not just literal
return a + b;
}
console.log(example()); // 1 + (1 + 1) = 3
console.log(example(2)); // 2 + (2 + 1) = 5
重要的原因之一是,如果参数列表和函数体只有一个作用域,则意味着参数列表中的表达式可以引用函数体中声明的提升变量和函数。提升的变量只有值undefined
,所以这没有用,但是提升的函数将用它们的函数体初始化,导致这样的情况:
// Doesn't work because of the additional scope
function example(a, b = blarg()) {
function blarg() {
return 42;
}
// ...
}
这会导致 ReferenceError,因为函数体的作用域在参数列表的作用域中不可用。
IIRC 有赞成和反对的争论,也有相当多的讨论,但最终决定将参数列表放在自己的范围内,以防止出现这种情况和其他奇怪的情况。