在 IIFE 中声明一个变量两次
declaring a variable twice in IIFE
我在互联网上完成了这个有趣的测验。
console.log((function(x, f = (() => x)){
var x;
var y = x;
x = 2;
return [x, y, f()]
})(1))
选项是:
[2,1,1]
[2, 未定义, 1]
[2, 1, 2]
[2, 未定义, 2]
我选择了解决方案 2 TBH,基于 x 已被重新定义,y 被声明并定义为没有值,并且 f 具有不同的范围,因此获得全局 x 内存点而不是函数 x 内存点。
不过,我在jsbin.com
试过了
我发现它是解决方案 1,虽然我不确定为什么会发生这种情况,但我弄乱了函数体并从函数体中删除了 var x
,我发现响应更改为 #3,这当 x 值发生变化时有意义,因此它显示 x 和 f 为 2,y 为 1,这是全局声明的。
但我仍然不明白为什么它显示 1 而不是未定义。
该代码的棘手部分是 =>
函数是作为默认参数值表达式的一部分创建的。在参数默认值表达式中,作用域包括左侧声明的参数,在本例中包括参数 x
。因此,出于这个原因,=>
函数中的 x
实际上是第一个参数。
该函数仅使用一个参数 1
调用,因此当 =>
函数被调用时就是这样 returns,给出 [2, 1, 1]
.
var x
声明,正如 Crowder 先生指出的那样,具有(有点奇怪,至少对我而言)产生 new x
的效果在函数作用域中,参数 x
的值被复制到其中。没有它,就只有一个(参数)。
but still I can't get why it shows 1 instead of undefined.
不只是你。这是规范中很深、很暗的部分。 :-)
这里的关键是有两个x
。对真的。有参数 x
,还有变量 x
.
包含表达式的参数列表(如f
的默认值)有其自己的作用域,与函数分开body的范围。但是在参数列表可能具有表达式之前,在具有 x
参数的函数中使用 var x
没有任何效果(x
仍然是参数,具有参数的值)。因此,为了保留这一点,当其中有一个包含表达式的参数列表时,将创建一个单独的变量,并将参数的值复制到函数开头的变量body.这就是这种 seemingly-odd (不,不仅仅是表面上) 奇怪行为的原因。 (如果您是那种喜欢深入研究规范的人,此复制是 FunctionDeclarationInstantiation 的第 28 步。)
由于 f
的默认值 () => x
是在参数列表范围内创建的,因此它引用 参数 x
,而不是 var.
所以第一个解[2, 1, 1]
是正确的,因为:
2
已分配给函数 body 中的变量 x
。所以在函数的最后,var x
是 2
.
在 x
得到值 2
之前,1
从 var x
赋值给 y
,所以在函数的最后,y
是 1
.
- 参数
x
的值从未改变,所以f()
在函数末尾导致1
好像代码是这样写的(我删除了不必要的parens并添加了缺少的分号):
console.log(function(param_x, f = () => param_x) {
var var_x = param_x;
var y = var_x;
var_x = 2;
return [var_x, y, f()];
}(1));
...I removed var x from the function body, I found that the response changed to #3...
#3 是 [2, 1, 2]
。这是正确的,因为当你从函数中删除 var x
时,只有一个 x
,参数(由参数列表中的函数 body 继承)。因此,将 2
分配给 x
会更改参数的值,即 f
returns.
以较早的 param_x
和 var_x
为例,如果您从中删除 var x;
,则如下所示:
console.log(function(param_x, f = () => param_x) {
var y = param_x;
param_x = 2;
return [param_x, y, f()];
}(1));
这是原始代码的注释说明(删除了多余的括号并添加了缺少的分号):
// /---- the parameter "x"
// v vvvvvvvvvvv--- the parameter "f" with a default value
console.log(function(x, f = () => x) {
var x; // <=== the *variable* x, which gets its initial value from the
// parameter x
var y = x; // <=== sets y to 1 (x's current value)
x = 2; // <=== changes the *variable* x's value to 2
// +---------- 2, because this is the *variable* x
// | +------- 1, because this is the variable y
// | | +--- 1, because f is () => x, but that x is the *parameter* x,
// | | | whose value is still 1
// v v vvv
return [x, y, f()];
}(1));
关于您的标题的最后说明:
declaring a variable twice in IIFE
变量只声明一次。另一件事是参数,而不是变量。区别很少是重要的……这是那些罕见的时期之一。 :-)
我在互联网上完成了这个有趣的测验。
console.log((function(x, f = (() => x)){
var x;
var y = x;
x = 2;
return [x, y, f()]
})(1))
选项是:
[2,1,1]
[2, 未定义, 1]
[2, 1, 2]
[2, 未定义, 2]
我选择了解决方案 2 TBH,基于 x 已被重新定义,y 被声明并定义为没有值,并且 f 具有不同的范围,因此获得全局 x 内存点而不是函数 x 内存点。
不过,我在jsbin.com
试过了我发现它是解决方案 1,虽然我不确定为什么会发生这种情况,但我弄乱了函数体并从函数体中删除了 var x
,我发现响应更改为 #3,这当 x 值发生变化时有意义,因此它显示 x 和 f 为 2,y 为 1,这是全局声明的。
但我仍然不明白为什么它显示 1 而不是未定义。
该代码的棘手部分是 =>
函数是作为默认参数值表达式的一部分创建的。在参数默认值表达式中,作用域包括左侧声明的参数,在本例中包括参数 x
。因此,出于这个原因,=>
函数中的 x
实际上是第一个参数。
该函数仅使用一个参数 1
调用,因此当 =>
函数被调用时就是这样 returns,给出 [2, 1, 1]
.
var x
声明,正如 Crowder 先生指出的那样,具有(有点奇怪,至少对我而言)产生 new x
的效果在函数作用域中,参数 x
的值被复制到其中。没有它,就只有一个(参数)。
but still I can't get why it shows 1 instead of undefined.
不只是你。这是规范中很深、很暗的部分。 :-)
这里的关键是有两个x
。对真的。有参数 x
,还有变量 x
.
包含表达式的参数列表(如f
的默认值)有其自己的作用域,与函数分开body的范围。但是在参数列表可能具有表达式之前,在具有 x
参数的函数中使用 var x
没有任何效果(x
仍然是参数,具有参数的值)。因此,为了保留这一点,当其中有一个包含表达式的参数列表时,将创建一个单独的变量,并将参数的值复制到函数开头的变量body.这就是这种 seemingly-odd (不,不仅仅是表面上) 奇怪行为的原因。 (如果您是那种喜欢深入研究规范的人,此复制是 FunctionDeclarationInstantiation 的第 28 步。)
由于 f
的默认值 () => x
是在参数列表范围内创建的,因此它引用 参数 x
,而不是 var.
所以第一个解[2, 1, 1]
是正确的,因为:
2
已分配给函数 body 中的变量x
。所以在函数的最后,varx
是2
.
在 1
从 varx
赋值给y
,所以在函数的最后,y
是1
.- 参数
x
的值从未改变,所以f()
在函数末尾导致1
x
得到值 2
之前,好像代码是这样写的(我删除了不必要的parens并添加了缺少的分号):
console.log(function(param_x, f = () => param_x) {
var var_x = param_x;
var y = var_x;
var_x = 2;
return [var_x, y, f()];
}(1));
...I removed var x from the function body, I found that the response changed to #3...
#3 是 [2, 1, 2]
。这是正确的,因为当你从函数中删除 var x
时,只有一个 x
,参数(由参数列表中的函数 body 继承)。因此,将 2
分配给 x
会更改参数的值,即 f
returns.
以较早的 param_x
和 var_x
为例,如果您从中删除 var x;
,则如下所示:
console.log(function(param_x, f = () => param_x) {
var y = param_x;
param_x = 2;
return [param_x, y, f()];
}(1));
这是原始代码的注释说明(删除了多余的括号并添加了缺少的分号):
// /---- the parameter "x"
// v vvvvvvvvvvv--- the parameter "f" with a default value
console.log(function(x, f = () => x) {
var x; // <=== the *variable* x, which gets its initial value from the
// parameter x
var y = x; // <=== sets y to 1 (x's current value)
x = 2; // <=== changes the *variable* x's value to 2
// +---------- 2, because this is the *variable* x
// | +------- 1, because this is the variable y
// | | +--- 1, because f is () => x, but that x is the *parameter* x,
// | | | whose value is still 1
// v v vvv
return [x, y, f()];
}(1));
关于您的标题的最后说明:
declaring a variable twice in IIFE
变量只声明一次。另一件事是参数,而不是变量。区别很少是重要的……这是那些罕见的时期之一。 :-)