在IIFE中两次声明变量

Ham*_*med 20 javascript iife

我在互联网上通过了这个有趣的测验。

console.log((function(x, f = (() => x)){
  var x;
  var y = x;
  x = 2;
  return [x, y, f()]
})(1))
Run Code Online (Sandbox Code Playgroud)

选择是:

  1. [2,1,1]

  2. [2,未定义,1]

  3. [2,1,2]

  4. [2,未定义,2]

我选择了解决方案2 TBH,基于x已被重新定义,y被声明和定义为没有值的情况,并且f具有不同的作用域,因此获得的全局x内存点比函数x内存点大。

但是,我在jsbin.com中尝试过

我发现这是解决方案1,虽然不确定为什么会弄乱函数主体并var x从函数主体中删除,但发现响应更改为#3,这在x值更改时是有意义的,因此表明x和f为2,y为1,这是全局声明的。

但我仍然不明白为什么它显示1而不是未定义。

T.J*_*der 24

但我仍然不明白为什么它显示1而不是未定义。

不只是你 这是规范的深层部分。:-)

关键是两个x。对真的。有参数 x,有变量 x

包含表达式的参数列表(如f的默认值)具有与函数主体的范围分开的自己的范围。但是在可能具有表达式的参数列表之前,在具有参数var x的函数内x没有任何效果(x仍然是具有参数值的参数)。因此,为了保留它,当其中有一个带有表达式的参数列表时,将创建一个单独的变量,并将参数的值复制到函数主体开头的变量中。这就是这种看似奇怪 (不只是看似)奇怪行为的原因。(如果您是那种喜欢介绍规范的人,则此复制是步骤28FunctionDeclarationInstantiation。)

由于f的默认值() => x在参数列表范围内创建,因此它引用的是参数 x,而不是var。

因此第一个解决方案[2, 1, 1]是正确的,因为:

  • 2被分配给x函数主体中的var 。因此,在函数末尾,var x2
  • 1在获得值之前y从var 分配给,所以在函数末尾是。xx2y1
  • 参数 x的值从来没有改变,因此f()导致1在该函数结束

这是因为,虽然代码是这样写的,而不是(我已经删除不必要的括号并添加缺少的分号):

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));
Run Code Online (Sandbox Code Playgroud)

...我从函数主体中删除了var x,发现响应更改为#3 ...

#3是[2, 1, 2]。是正确的,因为当您var x从函数中删除时,只有一个x参数(从参数列表中的函数主体继承)。因此,分配2给会x更改参数的值,然后f返回。

服用earier例如用param_xvar_x,这里是什么样子,如果你删除喜欢var x;的吧:

console.log(function(param_x, f = () => param_x) {
  var y = param_x;
  param_x = 2;
  return [param_x, y, f()];
}(1));
Run Code Online (Sandbox Code Playgroud)


这是原始代码的带注释的说明(删除了多余的括号并添加了缺少的分号):

//                   /---- 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));
Run Code Online (Sandbox Code Playgroud)

关于标题的最后说明:

在IIFE中两次声明变量

变量仅声明一次。另一件事是参数,而不是变量。区别很少重要...这是那些罕见的时期之一。:-)

  • 现在,这列在“关于JavaScript的新奇事物”的列表中,以及“类”声明中实例属性初始化表达式的语义(尚未真正成为标准)。 (2认同)