【问题标题】:Why is the function argument not overwritten on creating variable of same name inside the function?为什么在函数内部创建同名变量时不会覆盖函数参数?
【发布时间】:2026-01-22 02:55:02
【问题描述】:
var a = 'why is this not undefined?';
function checkScope(a) {
    var a;
    console.log(a);
}
checkScope(a);

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

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

【问题讨论】:

  • 我不确定,但我认为,由于提升,两个 a var 在调用函数之前都已声明,因此参数 a 变为与声明的 @987654324 相同的 var @函数内部。
  • 什么是“功能范围”? JS 具有函数作用域,即参数名称、函数和变量声明定义了整个函数体的变量名称。这里没有任何东西互相覆盖。
  • JS 语法允许这样做!?

标签: javascript variables function-parameter


【解决方案1】:

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

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

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

假设你有var a = 1;,而不是var a;,在这种情况下,变量已经声明,但赋值表达式将在运行时计算,值1将是分配给a。所以,console.log 会打印出1


此行为在 ECMA 脚本 5.1 规范中的 10.5 Declaration Binding Instantiation 部分下进行了解释,

  1. 如果code是功能码,那么

    一个。令 func 为其 [[Call]] 内部方法启动 code 执行的函数。令 namesfunc 的值 [[FormalParameters]] 内部属性。

    b.令 argCount 为 args 中的元素个数。

    c。让 n 为数字 0。

    d。对于 names 中的每个字符串 argName,按列表顺序执行

          我。令 nn 的当前值加 1。

          ii.如果 n 大于 argCount,则令 v 未定义,否则令 v 的值args 的第 n 个元素。

          iii. argAlreadyDeclared 成为调用 env 的 HasBinding 具体方法并传递 argName 作为参数的结果。

          iv. 如果 argAlreadyDeclared 为 false,则调用 env 的 CreateMutableBinding 具体方法,将 argName 作为 论据。

          v. 调用 env 的 SetMutableBinding 具体方法,传递 argNamevstrict 作为参数.

....

  1. 对于 code 中的每个 VariableDeclarationVariableDeclarationNoIn d,按源文本顺序执行

    一个。设 dnd 中的 Identifier

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

    c。 如果 varAlreadyDeclared 为假,则

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

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

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

【讨论】:

    【解决方案2】:

    参数仍然在函数内部定义,因为当变量已经在同一范围内定义时,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);

    【讨论】:

      【解决方案3】:
      1. 您将变量 a 作为参数传递给函数检查。function checkScope(a) {

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

      这两者都在同一个范围内,对吗?即都在函数 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,因此变为未定义

      【讨论】:

        【解决方案4】:

        因为 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 中描述了此行为。

        【讨论】: