为什么在函数内部创建同名变量时不会覆盖函数参数?

时间:2015-01-15 12:14:55

标签: javascript variables function-parameter

var a = 'why is this not undefined?';
function checkScope(a) {
    var a;
    console.log(a);
}
checkScope(a);

Javascript是功能范围语言,对吗?当我在与函数参数使用相同名称的函数内部声明一个新变量时,为什么新定义的变量仍然保存与参数相同的数据?

我认为它应该是未定义的?

4 个答案:

答案 0 :(得分:41)

var a;实际上是一个变量声明语句。定义函数时,在代码执行之前处理其中声明的所有变量,因此即使在运行时执行实际声明行之前也可以使用变量。这称为var hoisting。因此,无论您声明一个变量多少次,该变量实际上只被声明一次。

在您的情况下,您已将a定义为函数的参数之一,该函数的作用域为当前函数。然后你声明一个具有相同名称的变量。由于a已在函数中声明,因此作为其中一个参数,var a;声明将被忽略。

这就是你在控制台中获得why is this not undefined?的原因。

而不是var a;,假设您有var a = 1;,在这种情况下,变量已经声明,但赋值表达式将在运行时和值{ {1}}将被分配到1。因此,a将打印console.log


此行为在ECMA脚本5.1规范的10.5 Declaration Binding Instantiation

部分中进行了解释
  
      
  1. 如果代码是功能代码,那么

         

    一个。设func是[[Call]]内部方法启动代码执行的函数。让名称 func 的值   [[FormalParameters]]内部财产。

         

    湾设 argCount 为args中的元素数。

         

    ℃。设 n 为数字0。

         

    d。对于名称中的每个String argName ,按列表顺序执行

         

    我。设 n n 的当前值加1。

         

    II。如果 n 大于 argCount ,请将 v 取消定义,否则让 v 的值n args的元素

         

    III。 argAlreadyDeclared 是调用 env 的HasBinding具体方法传递 argName 作为参数的结果。

         

    IV。 如果 argAlreadyDeclared 为false,请调用 env 的CreateMutableBinding具体方法,将 argName 作为   参数

         

    v。调用 env 的SetMutableBinding具体方法,将 argName v strict 作为参数传递

  2.   

...

  
      
  1. 对于代码中的每个 VariableDeclaration VariableDeclarationNoIn d ,在源文本顺序中执行

         

    一个。让 dn 成为 d 中的标识符

         

    湾让 varAlreadyDeclared 成为调用 env 的HasBinding具体方法传递 dn 作为参数的结果。

         

    ℃。 如果 varAlreadyDeclared 为false,则

         

    我。 调用 env 的CreateMutableBinding具体方法,将 dn configurableBindings 作为参数传递。

         

    II。调用 env 的SetMutableBinding具体方法,将 dn ,undefined和 strict 作为参数传递。

  2.   

正如我们在规范中看到的那样,函数中声明的参数和变量,实际上都是在与定义它们的函数相对应的执行环境中定义的。因此,如果参数和变量具有相同的名称,则该变量实际上只定义一次,而忽略第二个声明。

答案 1 :(得分:4)

  1. 您将变量a作为参数传递给函数检查。function checkScope(a) {

  2. 在函数内部,您再次尝试声明var a,var a;

  3. 这两个都在同一范围内,对吧?即函数check();

    内部

    根据docs,函数内的var a是你作为参数传递的同一个变量,你只是在声明之前使用它...你可以用JS做到这一点

    所以你编写的代码与

    是等价的
    var a = 'why is this not undefined?';
    function checkScope(a) {
    
        console.log(a);
    }
    checkScope(a);
    

    ,即var a被忽略。

    你期望var a应该返回undefined然后代码就像这样

    var a = 'why is this not undefined?';
    function checkScope() {
       var a
        console.log(a);
    }
    checkScope();
    

    这次我们没有将参数a传递给函数,并且在函数范围内创建了一个新的变量var a,因此变得未定义

答案 2 :(得分:4)

该参数仍在函数内部定义,因为当变量已在同一范围内定义时,var a;被忽略。

var a;这样的语句并不代表在代码中的那一点创建变量。所有变量声明都是hoisted到示波器的顶部,因此您可以在示波器中重新声明它们多次,并且它们仍然只创建一次。

如果声明具有赋值,例如var a = 2;,则赋值发生在语句在代码中的位置,无论声明是否被忽略。

示例:



function check(a) {
  // both a and b exist here
  document.write('a = ' + a + ', b = ' + b + '<br>');
  var b = 1;
  document.write('a = ' + a + ', b = ' + b + '<br>');
  var a = 2; // not recreated, but assigned
  document.write('a = ' + a + ', b = ' + b + '<br>');
}

check(42);
&#13;
&#13;
&#13;

答案 3 :(得分:2)

因为JavaScript忽略了变量的重新声明。但是,如果您改为:

var a = 'why is this not undefined?';
function checkScope(a) {
    var a = 'foo';
    console.log(a);
}
checkScope(a);

a的值将被覆盖。因为var a = 'foo';由变量声明(var a;)和值赋值(a = 'foo';)组成;

MDN docs

中描述了此行为