【问题标题】:JavaScript fundamentals confusion [duplicate]JavaScript基础知识混乱[重复]
【发布时间】:2017-04-22 06:26:42
【问题描述】:

您好,我正在尝试了解 JavaScript 基础知识,但遇到了一种情况。

    var foo = 1;
    function bar(){
        foo = 10;       
        return;
        function foo(){}
    }
    bar();
    alert(foo);

这里alert(foo),给我1,我知道在return语句之后,函数foo()不会执行。但是现在如果更改代码:

    var foo = 1;
    function bar(){
        foo = 10;       
        return;
    }
    bar();
    alert(foo); 

在 bar 函数中,如果我将删除函数 foo()。然后alert(foo) 会给我10

请帮忙,如果有人可以解释一下为什么?

【问题讨论】:

  • 究竟有什么疑问?
  • 可能函数名和变量名不能相同。
  • 第二个代码中的警报输出是否为 10?如果我猜对了,那么它应该是 10,因为变量 foo 在这个函数中也有它的作用域
  • function foo(){} 是否意味着变量foo 将被初始化??????

标签: javascript


【解决方案1】:

所以基本上发生的事情就像你声明了var foo = 10 因为 javascript 中的函数声明被提升到顶部 编译器看到您的代码如下。

var foo = 1;
function bar(){
  var foo;
  foo = 10;       
  return;
  function foo(){}
}
bar();
alert(foo);

所以事实上foo = 10 永远不会覆盖全局foo; 它保留在函数的本地。 所以 alert 将通过 global 。

【讨论】:

    【解决方案2】:

    除了我在同一线程中的 previous answer 添加另一个答案以提供有关吊装的更多详细信息 JavaScript 中的功能,因为之前的答案已被 OP 接受。

    首先让我们熟悉一下什么是作用域

    JavaScript 中的作用域

    对于 JavaScript 初学者来说,最困惑的根源之一是范围。实际上,这不仅仅是初学者。我遇到过很多经验丰富的 JavaScript 程序员,他们并不完全理解作用域。 JavaScript 中范围界定如此混乱的原因是它看起来像 C 系列语言。考虑以下 C 程序:

    #include <stdio.h>
    int main() {
        int x = 1;
        printf("%d, ", x); // 1
        if (1) {
            int x = 2;
            printf("%d, ", x); // 2
        }
        printf("%d\n", x); // 1
    }
    

    该程序的输出将是 1、2、1。这是因为 C 和 C 家族的其余部分具有块级范围。当控制进入一个块时,例如 if 语句,可以在该范围内声明新变量,而不会影响外部范围。在 JavaScript 中情况并非如此。在 Firebug 中尝试以下操作:

    var x = 1;
    console.log(x); // 1
    if (true) {
        var x = 2;
        console.log(x); // 2
    }
    console.log(x); // 2
    

    在这种情况下,Firebug 将显示 1、2、2。这是因为 JavaScript 具有函数级范围。这与 C 系列完全不同。块,例如 if 语句,不会创建新范围。只有函数会创建新范围。

    对于许多习惯于 C、C++、C# 或 Java 等语言的程序员来说,这是出乎意料且不受欢迎的。幸运的是,由于 JavaScript 函数的灵活性,有一种解决方法。如果您必须在函数中创建临时范围,请执行以下操作:

    function foo() {
        var x = 1;
        if (x) {
            (function () {
                var x = 2;
                // some other code
            }());
        }
        // x is still 1.
    }
    

    这种方法实际上非常灵活,可以在需要临时作用域的任何地方使用,而不仅仅是在块语句中。但是,我强烈建议您花时间真正理解和欣赏 JavaScript 作用域。它非常强大,也是我最喜欢的语言功能之一。如果您了解范围界定,那么提升对您来说会更有意义。


    声明、名称和吊装

    在 JavaScript 中,名称以四种基本方式之一进入范围:

    Language-defined: 默认情况下,所有作用域都赋予名称 this 和参数。

    Formal parameters: 函数可以具有命名形式参数,其作用域为该函数的主体。

    • 函数声明:格式为function foo() {}
    • 变量声明:采用var foo; 的形式。
    • 函数声明和变量声明总是被 JavaScript 解释器以不可见的方式移动(“提升”)到其包含范围的顶部
    • 显然,函数参数和语言定义的名称已经存在。这意味着这样的代码:

    例如:

    function foo() {
            bar();
            var x = 1;
        }
    

    实际上是这样解释的:

    function foo() {
        var x;
        bar();
        x = 1;
    }
    

    事实证明,包含声明的行是否会被执行并不重要。以下两个函数是等价的:

    function foo() {
        if (false) {
            var x = 1;
        }
        return;
        var y = 1;
    }
    
    function foo() {
        var x, y;
        if (false) {
            x = 1;
        }
        return;
        y = 1;
    }
    

    请注意,声明的赋值部分没有被提升。 只吊起名字。这不是函数声明的情况,整个函数体也将被提升。但请记住,声明函数有两种常规方式。考虑以下 JavaScript:

    function test() {
        foo(); // TypeError "foo is not a function"
        bar(); // "this will run!"
        var foo = function () { // function expression assigned to local variable 'foo'
            alert("this won't run!");
        }
        function bar() { // function declaration, given the name 'bar'
            alert("this will run!");
        }
    }
    test();
    

    在这种情况下,只有函数声明的主体被提升到顶部。名称“foo”被提升,但主体被留下,在执行期间被分配。

    这涵盖了提升的基础知识。此答案的完整 100% 归功于 ben cherry。我不想在我的答案中发布此链接,因为链接可能会断开,我发现此内容非常有用,任何 javascript 开发人员都必须阅读。

    【讨论】:

      【解决方案3】:

      这称为 Javascript 提升

      我会尽量详细解释..这就是我们所拥有的

      var foo = 1;
      
      function bar(){
        foo = 10;       
        return;
        function foo(){}
      }
      
      bar();
      alert(foo);
      

      解释器会将其重写为

      var foo = 1;
      
      function bar(){
        function foo(){}  // this is also equal to var foo = function(){};
        foo = 10;       
        return;      
      }
      
      bar();
      alert(foo);
      

      所以现在向您解释提升的代码。

      var foo = 1; // global variable; 
      
      function bar(){
        var foo = function(){};  // foo is local variable of type function
        foo = 10;                // foo is changed to hold a number
        return;      
      }
      
      bar();
      alert(foo);  // you alert global variable.
      

      如您所见,如果代码 function foo(){} 存在,则它被视为 bar() 范围内的局部变量,对 foo 的任何更改都被视为局部变量更改。

      • 当您在您的bar() function foo(){} 时,您甚至不会触及全局变量.. 因此会提醒1

      • 当您没有 function foo(){} 时,您正在触摸全局变量并因此提醒10

        现在我希望你理解输出..

      【讨论】:

      • 这里值得一提的是,像foo = function(){}; 这样的函数表达式不会被提升到作用域的开头,因此它们在出现在代码中之前不能使用,这意味着如果你这样做function bar(){ foo = 10; return; foo = function(){} } foo 会返回 10
      • 赞的解释很好 :) 但是你怎么知道解释器会将这段代码转换成另一个代码??
      • @Mahi 因为这就是 javascript 解释器的工作方式。它被称为“吊装”,是一个常见的概念。查一下。
      • @Dellirium javascript解释器是javascript引擎??
      • @phobia82 这是一个很好的观点。
      【解决方案4】:

      这里的问题是hoistingclosure

      声明function foo(){} 被提升,这意味着在这种情况下,即使它写在函数的末尾,它在范围内的任何地方都可用,包括在它定义之前。

      如果function foo(){} 不存在,语句foo = 10; 将覆盖在全局范围内定义的foo。因此全局foo === 10

      如果存在function foo(){},则语句foo = 10; 只会覆盖本地范围内的函数foo,全局foo 不会被触及,因此全局foo === 1

      var foo = 1;
      function bar(){
         console.log(typeof foo) // function      
         return;
         function foo() {}
      }
      bar();
      alert(foo); 
      

      反对:

      var foo = 1;
      function bar(){
         console.log(typeof foo) // number      
         return;
         // function foo() {}
      }
      bar();
      alert(foo); 
      

      【讨论】:

        【解决方案5】:

        当你写这个函数时:

        function bar(){
            foo = 10;       
            return;
            function foo(){}
        }
        

        javascript 读到这个:

        function bar(){
            function foo(){}
            foo = 10;       
            return;
        }
        

        函数foo 被创建到您的本地函数bar 中。而当你写foo = 10时,你会覆盖局部作用域中的函数foo而不是全局变量。

        所以你的警报给你 1,因为你从不更新全局变量。

        【讨论】:

          【解决方案6】:

          我知道return语句后,函数foo()不会执行。

          这不是真的。

          函数声明被提升。

          function foo(){} 创建一个名为foo 的局部变量(将新函数分配给它),然后foo = 10 覆盖它。不过,您永远不会测试 foo 变量的值。

          在 bar 函数中,如果我将删除函数 foo()。然后 alert(foo) 会给我 10

          您不再有一个名为 foo 的局部变量,因此您将覆盖同名的全局变量。


          比较:

          (function() {
            console.log("Version 1");
            var foo = 1;
          
            function bar() {
              console.log("At top of bar, foo is " + foo);
              foo = 10;
              console.log("After assignment in bar, foo is " + foo);
              return;
          
              function foo() {}
            }
            bar();
            console.log("Global foo is " + foo);
          }());
          
          (function() {
            console.log("Version 2");
            var foo = 1;
          
            function bar() {
              console.log("At top of bar, foo is " + foo);
              foo = 10;
              console.log("After assignment in bar, foo is " + foo);
              return;
            }
            bar();
            console.log("Global foo is " + foo);
          }());

          【讨论】:

          • “那不是真的”在这里不是正确的答案。确实 alert(foo) 确实给了他 1,正如他所说,函数 foo 不会执行......该语句是 100% 正确的。您的回答显然是正确的,但是,请清除措辞。
          • @Dellirium — 函数声明将执行,它将创建一个函数。该函数永远不会被调用,但创建它的语句仍会执行。
          • 这是正确的,为了帮助 OP 清除这一点,在您设置 foo = 10 之前写入 console.log(foo) 它应该设置为您在返回后声明的 foo 函数。
          • 我为进一步澄清做了一个小提琴jsfiddle.net/2rzvu18q/1
          猜你喜欢
          • 1970-01-01
          • 2012-10-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-23
          • 1970-01-01
          相关资源
          最近更新 更多