【问题标题】:Why do some variables declared using let inside a function become available in another function, while others result in a reference error?为什么在函数中使用 let 声明的某些变量在另一个函数中可用,而另一些则导致引用错误?
【发布时间】:2020-01-27 12:58:26
【问题描述】:

我不明白为什么在函数中声明变量时会表现得如此奇怪。

  1. first 函数中,我使用let 声明变量bc,其值为10

    b = c = 10;
    

    在我展示的second函数中:

    b + ", " + c
    

    这表明:

    10, 10
    
  2. 同样在first 函数中,我声明a 的值为10

    let a = b = c = 10;
    

    但在second 函数中却显示错误:

    找不到变量:a

  3. 现在在first 函数中,我声明d 的值为20

    var d = 20;
    

    但在second 函数中,它显示与以前相同的错误,但变量d

    找不到变量:d

例子:

function first() {
  let a = b = c = 10;
  var d = 20;
  second();
}

function second() {
  console.log(b + ", " + c); //shows "10, 10"

  try{ console.log(a); }  // Rreference error
  catch(e){ console.error(e.message) }

  try{ console.log(d); } // Reference error
  catch(e){ console.error(e.message) }
}
first()

【问题讨论】:

  • 你在声明全局变量,因为 bc 没有前缀 var 关键字。 adfirst 本地的。
  • 评论不用于扩展讨论;关于这是否是一个好的面试问题的切题对话是archived in chat
  • 这让我想起了Visual Basic中的类似情况; Dim Apple, Banana, Pear As Fruit 表示Dim Apple / Dim Banana / Dim Pear As Fruit,而不是Dim Apple As Fruit / ...

标签: javascript variable-declaration


【解决方案1】:

因为你实际上是在说:

c = 10;
b = c;
let a = b;

并不是你认为你在说什么,而是:

let a = 10;
let b = 10;
let c = 10;

您会注意到,无论您向链中添加多少变量,它只会是导致错误的第一个 (a)。

这是因为“让”将您的变量限定在您声明它的块(或“本地”,或多或少意味着“在括号中”)。

如果你声明一个没有“let”的变量,它会在全局范围内定义变量。

因此,在您设置变量的函数中,所有内容的值都为 10(如果您设置了断点,您可以在调试器中看到这一点)。如果您在第一个函数中放置 a、b、c 的控制台日志,那么一切都很好。

但是一旦你离开那个函数,第一个 (a)——再一次记住,从技术上讲,按照分配的顺序,它是最后一个——“消失”(再次,你可以看到如果您在第二个函数中设置断点,则在调试器中使用 this),但其他两个(或添加的任何数量)仍然可用。

这是因为,“让”仅适用于(因此仅适用于本地范围)第一个变量——同样,它在技术上是最后一个被声明并分配值的变量——在链中。其余的技术上没有“让”在他们面前。因此,它们在技术上是全局声明的(即在全局对象上),这就是它们出现在您的第二个函数中的原因。

试试看:去掉“let”关键字。您的所有变量现在都将可用。

"var" 具有类似的局部作用域效果,但变量“提升”的方式不同,这是您绝对应该理解的,但与您的问题没有直接关系。

(顺便说一句,这个问题会难倒足够多的专业 JS 开发者,使其成为一个好的问题)。

强烈建议您花时间了解在 JS 中如何声明变量的不同之处:不使用关键字、使用“let”和使用“var”。

【讨论】:

  • 编程同时也是最好和最坏的事情:计算机会完全按照你告诉它去做。不一定是你打算告诉它做什么。程序很完美。我们制造了问题。
  • @Thevs 为什么在这个答案的上下文中推荐var 而不是let?我不明白。
  • @Thevs 我非常不同意你的观点。 var 如果使用不小心,可能会出现错误。检查这个fiddle
  • @Thevs 在什么情况下varlet任何 优势?让我澄清一下:当两者都是选项并且我要求一个应该编写的代码时的现代上下文。当我之前问过这个问题时,我收到了有关“您可以使用 var 重新声明变量”的答案,在这种情况下,我必须提醒人们 您不应该重新声明变量我>。那是代码逻辑中的错误或错误-因此重新声明的好处是……它允许您编写错误的代码。当let 也是一种选择时,我还没有看到任何支持var 的合理理由。
  • 用 javascript 标记另一个标记。除非声明为本地变量,否则所有变量都是全局变量。 :(
【解决方案2】:

在函数first()中,变量bc是动态创建的,不使用varlet

let a = b = c = 10; // b and c are created on the fly

不同于

let a = 10, b = 10, c = 10; // b and c are created using let (note the ,)

它们成为隐式全局。这就是为什么它们可以在second()

来自documentation

给一个未声明的变量赋值会在赋值执行时隐式地把它创建为一个全局变量(它成为全局对象的一个​​属性)。

为避免这种情况,您可以使用"use strict",它会在使用未声明的变量时提供错误

"use strict"; // <-------------- check this

function first() {
   /*
    * With "use strict" c is not defined.
    * (Neither is b, but since the line will be executed from right to left,
    * the variable c will cause the error and the script will stop)
    * Without, b and c become globals, and then are accessible in other functions
    */
   let a = b = c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //reference error
   console.log(a); //reference error
   console.log(d); //reference error
}

first();

【讨论】:

  • 另外:let a = 10, b = 10, c = 10;let a, b, c; a = b = c = 10; 否则是声明变量的正确方式。
  • 那么在严格模式下,变量b呢?
  • @Tick20 变量b 不会被评估/到达,错误将出现在let a = b = c = 10; 行,从从右到左读取。 c 是导致ReferenceError 的第一个变量,该行的其余部分将不会执行(脚本已停止)
  • let a = 10, b = a, c = b; 之类的东西也有效
  • 主要支持“使用严格”。在面试问题的上下文中,这也是我对这段代码的评论的开始。
【解决方案3】:

在说事情奇怪之前,让我们先了解一些基础知识:

varlet 都用于 JavaScript 中的变量声明。例如,

var one = 1;
let two = 2;

也可以不使用varlet 来声明变量。例如,

three = 3;

现在上述方法的区别在于:

var 是函数作用域

let 是块作用域。

而没有var/let关键字声明的变量范围 成为全局,无论它在哪里声明。

Global variables 可以从网页的任何位置访问(不推荐,因为全局变量可能会被意外修改)。

现在根据这些概念,让我们看看有问题的代码:

 function first() {
   let a = b = c = 10;
   /* The above line means:
    let a=10; // Block scope
    b=10; // Global scope
    c=10; // Global scope
    */

   var d = 20; // Function scope
   second();
}

function second() {
   alert(b + ", " + c); // Shows "10, 10" //accessible because of global scope
   alert(a); // Error not accessible because block scope has ended
   alert(d); // Error not accessible because function scope has ended
}

【讨论】:

  • 你抄袭了JonoJames's answer的一部分。为什么?
  • 很抱歉,我没有这样的意图,可能存在类似的情况,因为我们可能从同一来源收集了一条信息。
  • 可能两个答案都包含从同一原始来源复制的内容,没有明显的引用——可能是tutorialsteacher.com/javascript/javascript-variable。剽窃的存在是显而易见的,因为语法错误是从原始文件中复制出来的——“没有var关键字声明的变量的范围变成全局的,不管它在哪里声明”应该是“范围...变为”或“范围...变为”。使用别人的确切词需要引用,无论是从这里还是从其他地方。 meta.stackexchange.com/q/160071/211183
  • 谢谢大家,我已经为源添加了参考链接。
【解决方案4】:

使用 let 关键字的变量应该只在块的范围内可用,而在外部函数中不可用...

您以这种方式声明的每个变量都没有使用letvar。您在变量声明中缺少逗号。

不建议声明没有var 关键字的变量。它可能会意外覆盖现有的全局变量。没有var 关键字声明的变量的范围变成全局的,而不管它在哪里声明。可以从网页中的任何位置访问全局变量。

function first() {
   let a = 10;
   let b = 10;
   let c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //shows "10, 10"
   console.log(a); //reference error
   console.log(d); //reference error
}

first();

【讨论】:

    【解决方案5】:

    这是因为当您不使用 letvar 时,变量会即时声明,最好像下面这样声明。

    let a = 10;
    let b = 10;
    let c = 10;
    

    【讨论】:

      【解决方案6】:

      这个奇怪的问题是由 JavaScript 中的作用域规则引起的

      function first() {
         let a = b = c = 10; // a is in local scope, b and c are in global scope
         var d = 20; // d is in local scope
         second(); // will have access to b and c from the global scope
      }
      

      假设您要声明 3 个初始化为相同值 (100) 的 局部变量。您的 first() 将如下所示。在这种情况下, second() 将无法访问任何变量,因为它们对于 first()

      本地
      function first() {
         let a = 100; // a is in local scope init to 100
         let b = a; // b is in local scope init to a
         let c = b // c is in local scope init to b
      
         var d = 20; // d is in local scope
         second(); // will not have access a, b, c, or d
      }
      

      但是,如果您想要全局变量,那么您的 first() 将如下所示。在这种情况下, second 将可以访问所有变量,因为它们在 global 范围内

      function first() {
         a = 100; // a is in global scope
         b = a; // b is in global scope
         c = b // c is in global scope
      
         d = 20; // d is in global scope
         second(); // will have access to a, b, c, and d from the global scope
      }
      

      局部变量(又名。可在声明它们的代码块中访问)。
      代码块是任何 {},其间有代码行。

      • function() {var, let, const in here 可以被整个函数访问},
      • for() {var in here 可被外部作用域访问,let, const 只能在此处访问},

      全局变量(也可以在全局范围内访问)。
      这些变量附加到全局对象。全局对象是环境相关的。它是浏览器中的窗口对象。

      特别说明:您可以在 JavaScript 中声明变量,而无需使用 var、let、const 关键字。以这种方式声明的变量附加到全局对象,因此可以在全局范围内访问。
      a = 100 // is valid and is in global scope

      一些文章供进一步阅读: https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ https://scotch.io/tutorials/understanding-scope-in-javascript https://www.digitalocean.com/community/tutorials/understanding-variables-scope-hoisting-in-javascript

      【讨论】:

        【解决方案7】:

        主要区别在于范围规则。由 var 关键字声明的变量的作用域是直接函数体(因此是函数作用域),而 let 变量的作用域是由 { } 表示的直接封闭块(因此是块作用域)。当你说

        c = 10;
        b = c;
        let a = b;
        

        c 和 b 的生命周期很有趣,但 a 只有块跨度,如果您尝试通过引用来访问 a,它总是显示错误,但 c 和 b 是全局的,所以它们不会。你会注意到没有无论您将多少变量添加到您的链中,它只会是第一个 (a) 导致错误。这是因为“让”将您的变量范围限定为块(或者,“本地”,或多或少意味着“在括号”)在其中声明它。如果您声明一个没有“let”的变量,它会在全局范围内定义变量。因此,在您设置变量的函数中,所有内容的值都是 10(如果您可以在调试器中看到你放了一个断点)。如果您在第一个函数中放置 a、b、c 的控制台日志,一切都很好。但是一旦您离开该函数,第一个 (a) - 再次记住,技术上按顺序分配,它是最后一个 - “消失”(同样,如果您在第二个函数中设置断点,您可以在调试器中看到这一点),但其他两个(或添加的任何数量)仍然可用。

        【讨论】:

          【解决方案8】:

          以下是 JavaScript 中变量声明的 3 个有趣方面:

          1. var 将变量的范围限制在定义它的块中。 ('var' 适用于本地范围。)

          2. let 允许在块内临时覆盖外部变量的值。

          3. 简单地声明一个没有 varlet 的变量将使 全局变量,无论在何处声明。

          这是 let 的演示,它是该语言的最新添加:

          // File name:  let_demo.js
          
          function first() {
             a = b = 10
             console.log("First function:    a = " + a)
             console.log("First function:    a + b = " + (a + b))
          }
          
          function second() {
              let a = 5
              console.log("Second function:    a = " + a)
              console.log("Second function:    a + b = " + (a + b))
          }
          
          first()   
          
          second()
          
          console.log("Global:    a = " + a)
          console.log("Global:    a + b = " + (a + b))
          

          输出:

          $ node let_demo.js 
          
          First function:    a = 10
          First function:    a + b = 20
          
          Second function:    a = 5
          Second function:    a + b = 15
          
          Global:    a = 10
          Global:    a + b = 20
          

          说明:

          变量 ab 在 'first()' 中被声明,没有 var 或 let 关键字。

          因此,ab 是全局的,因此可以在整个程序中访问。 p>

          在名为 'second' 的函数中,语句 'let a = 5' 临时设置值从 'a' 到 '5',仅在函数范围内。

          在'second()'的范围之外,即在全局范围内,'a'的值将是之前定义的。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-01-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-03-01
            • 2022-01-08
            • 1970-01-01
            相关资源
            最近更新 更多