为什么在函数内部使用 let 声明的某些变量在另一个函数中可用,而其他变量会导致引用错误?

Why do some variables declared using let inside a function become available in another function, while others result in a reference error?

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

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

    b = c = 10;
    

    在我显示的second函数中:

    b + ", " + c
    

    这表明:

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

    let a = b = c = 10;
    

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

    Can't find variable: a

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

    var d = 20;
    

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

    Can't find variable: 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()

在函数 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

Assigning a value to an undeclared variable implicitly creates it as a global variable (it becomes a property of the global object) when the assignment is executed.

为避免这种情况,您可以使用 "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 关键字的变量应该只在块的范围内可用,在外部函数中不可用...

您以这种方式声明的每个变量都没有使用 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();

这是因为你实际上是在说:

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

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

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

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

这是因为 "let" 将您的变量范围限定在您声明它的块(或者,"locally",或多或少意味着 "in the brackets")。

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

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

但是一旦你离开那个函数,第一个 (a)——再次记住,从技术上讲,按照分配的顺序,它是最后一个——"disappears"(再次,如果您在第二个函数中设置断点,您可以在调试器中看到它),但其他两个(或无论您添加多少)仍然可用。

这是因为,"let" 仅适用于(因此仅在局部范围内)链中的第一个变量——同样,从技术上讲,这是最后一个声明并赋值的变量。其余的在技术上没有 "let" 在他们面前。所以这些在技术上是全局声明的(即在全局对象上),这就是它们出现在您的第二个函数中的原因。

试一试:删除 "let" 关键字。您所有的变量现在都可用。

"var" 具有类似的局部范围效果,但变量 "hoisted" 的方式不同,这是您绝对应该理解的,但与您的问题没有直接关系。

(顺便说一句,这个问题会让足够多的专业 JS 开发者感到困惑,无法让它成为一个好的问题)。

强烈建议您花点时间了解在 JS 中变量声明方式的差异:没有关键字、使用 "let" 和使用 "var"。

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

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

var one = 1;
let two = 2;

变量也可以在不使用 varlet 的情况下声明。例如,

three = 3;

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

var 是函数作用域

let 是块范围的。

while the scope of the variables declared without var/let keyword become global irrespective of where it is declared.

Global variables can be accessed from anywhere in the web page (not recommended because globals can be accidentally modified).

现在根据这些概念来看一下有问题的代码:

 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
}

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

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

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

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

c 和 b 的生命周期与 fun 一样,但 a 仅具有块跨度,如果您尝试通过引用访问 a,它总是会显示错误,但 c 和 b 是全局的,因此它们不会't.You'您会注意到,无论您向链中添加多少变量,它只会是导致 error.This 的第一个 (a),因为 "let" 将您的变量范围限定在块中(或者,"locally",或多或少的意思是 "in the brackets"),你在其中声明 it.If 你声明一个没有 "let" 的变量,它限定了变量 globally.So,在你设置你的函数中变量,所有值都为 10(如果放置断点,您可以在调试器中看到这一点)。如果您在第一个函数中放置 a、b、c 的控制台日志,那么一旦您离开该函数,所有内容都是 well.But,第一个 (a)——再次记住,从技术上讲,赋值顺序,它是最后一个——"disappears"(同样,如果你在第二个函数中设置断点,你可以在调试器中看到它),但其他两个(或者你添加的数量)仍然可用。

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

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

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

  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是全局的,因此可以在整个程序中访问。

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

在'second()'的范围之外,即在全局范围内,'a[=75的值=]' 将如前所述。

这个奇怪的问题是由 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() 将无法访问任何变量,因为它们是 local 到 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 scope

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是整个函数都可以访问的},
  • for() {这里的 var 可以被外部作用域访问,let, const 只能在这里访问},

全局变量(又名在全局范围内可访问)。
这些变量附加到全局对象。全局对象依赖于环境。它是浏览器中的 window 对象。

特别说明:您可以在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