在IIFE中两次声明变量

时间:2019-04-07 14:28:48

标签: javascript iife

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

       <?php
         if(!function_exists('ct_nav_left')) {
function ct_nav_left() { ?>
    <nav class="left">
        <?php wp_nav_menu (
            array(
                'menu'            => "primary-left",
                'menu_id'         => "ct-menu",
                'menu_class'      => "ct-menu",
                'echo'            => true,
                'container'       => '', 
                'container_class' => '',

    'items_wrap'      => '<ul id="ct-menu" class="ct-menu top_left_menu"> 
      <li id="menu-item-1447"><a href="#">Properties</a> 

    <div class="container">
      <div class="sub-menu">
     <div class="col-md-3 left_one_menu">
     <a href="#">All Properties</a>
      <a href="#/">Enquire Now</a>
       </div>

      <div class="col-md-3 left_two_menu">
        echo do_shortcode( "[contact-form-7 id="1234" title="Contact form 
      1"]" );
       </div>

     </div>
     </div>

       </li>

          </ul>',
                'container_id'    => 'nav-left', 
                'theme_location'  => 'primary_left',
                'fallback_cb'     => false,
                'walker'          => new 
      CT_Menu_Class_Walker
            )
        ); ?>
    </nav>
<?php }
      }



        ?>

和选择是:

  1. [2,1,1]

  2. [2,undefined,1]

  3. [2,1,2]

  4. [2,undefined,2]

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

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

,我发现它是解决方案1,虽然不确定为什么会弄乱函数主体,并从函数主体中删除了console.log((function(x, f = (() => x)){ var x; var y = x; x = 2; return [x, y, f()] })(1)) ,但发现响应更改为#3,这很有意义因为x值发生了变化,因此x和f显示为2,y显示为1,这是全局声明的。

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

2 个答案:

答案 0 :(得分:23)

  

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

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

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

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

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

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

  • 2被分配给函数主体中的变量x。因此,在函数末尾,变量x2
  • 1获得值y之前,将
  • x从变量x分配给2,因此在函数{{1 }}是y
  • 参数 1的值从未更改,因此x在函数结尾处产生f()

就像是 那样,代码是这样写的(我已经删除了不必要的括号并添加了缺少的分号):

1

  

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

#3是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));。是正确的,因为当您从函数中删除[2, 1, 2]时,只有一个var x这个参数(从参数列表中的函数主体继承)。因此,将x分配给2会更改参数的值,x将返回该值。

以使用fparam_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));

关于标题的最后说明:

  

在IIFE中两次声明变量

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

答案 1 :(得分:2)

该代码的棘手部分是将=>函数创建为默认参数值表达式的一部分。在参数默认值表达式中,范围包括在左侧声明的参数,在这种情况下,该参数包括参数x。因此,出于这个原因,x函数中的=>实际上是第一个参数。

仅使用一个参数1调用该函数,因此,在调用=>函数时,它返回的结果为[2, 1, 1]

正如克劳德先生所指出的那样,var x声明具有(至少对我来说有点奇怪)在函数范围内制作一个 new x的效果,将 parameter x的值复制到其中。没有它,就只有一个(参数)。