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


158

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

  1. first函数中,我使用let变量bc10进行声明

    b = c = 10;

    second函数中,我显示:

    b + ", " + c

    这表明:

    10, 10
  2. 同样在first函数中,我声明a10

    let a = b = c = 10;

    但是在second函数中它显示了一个错误:

    找不到变量: a

  3. 现在在first函数中,我声明d20

    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()


31
您在声明全局变量,因为bc没有前缀var关键字。a并且d是本地的first
VLAZ

1
评论不作进一步讨论;是否这将是一个很好的面试问题切线对话已被封存在聊天
科迪·格雷

1
这使我想起了Visual Basic中的类似情况。Dim Apple, Banana, Pear As Fruit是手段Dim Apple / Dim Banana / Dim Pear As Fruit,而不是Dim Apple As Fruit / ...
埃里克·利珀特

Answers:


179

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

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

而不是您想说的是:

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

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

这是因为“ let”将变量的作用域限定为声明变量的块(或“局部”,或多或少的意思是“在方括号中”)。

如果声明不带“ let”的变量,它将在全局范围内定义变量。

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

但是,一旦您离开该功能,第一个(a)-再一次,要记住,从技术上讲,按照分配的顺序,它是最后一个-“消失”(同样,您可以在调试器(如果您在第二个函数中设置了断点),但其他两个(或添加的数目)仍然可用。

这是因为,“ let”仅适用于(因此仅在本地范围内)链中的第一个变量-再次,从技术上讲,它是最后一个要声明并分配值的变量。从技术上讲,其余的都没有在前面。因此,它们在技术上是全局声明的(即在全局对象上),这就是为什么它们出现在第二个函数中的原因。

尝试一下:删除“ let”关键字。您所有的var现在都可以使用了。

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

(顺便说一句,这个问题将困扰足够的专业JS开发人员,使其成为一个好人)。

强烈建议您花时间在JS中声明变量的方式上有所不同:不带关键字,带“ let”和“ var”。


4
同时,这是编程过程中最好的和最糟糕的事情:计算机将完全按照您的指示进行操作。不一定要您打算告诉它做什么。程序是完美的。我们制造问题。
Niet the Dark Absol

8
@Thevs为何建议varlet这个答案的情况下?我不明白
Klaycon

4
@Thevs我非常不同意你。var如果使用不当,可能会产生错误。检查这个小提琴
Cid

2
@Thevs在什么情况下var什么优势let?让我澄清一下:这是一个现代的环境,当两者都是选项时,我要求一个人应该编写的代码。当我之前问过这个问题时,我收到了有关“您可以使用来重新声明变量var”的答案,在这种情况下,我不得不提醒人们您不应该重新声明变量。这要么是代码逻辑中的错误,要么是错误-因此,重新声明的好处是...它使您可以编写错误的代码。我还没有看到任何明智的理由支持“ var何时” let也是一种选择。
VLAZ

2
针对javascript加上另一个标记。除非声明为局部变量,否则所有变量都是全局变量。:(
JRE

68

在函数中first(),无需使用或即可动态创建变量b和。cvarlet

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()

文档

在执行赋值时,将值分配给未声明的变量会隐式地将其创建为全局变量(它成为全局对象的属性)。

为避免这种情况,您可以使用"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();


15
另外:let a = 10, b = 10, c = 10;let a, b, c; a = b = c = 10;否则会宣布变量的正确方法。
RickardElimää

那么在严格模式下,变量b呢?
Tick20

2
@ Tick20该变量b将不会被评估/到达,该错误将在行中发生let a = b = c = 10;从右到左读取。c作为导致的第一个变量ReferenceError,行的其余部分将不会执行(脚本已停止)
Cid

2
类似的东西let a = 10, b = a, c = b;也是有效的
Kaddath

8
主要针对“严格使用”进行投票。在面试问题中,这也是我对此代码发表评论的起点。
Pac0

23

在使事情变得奇怪之前,让我们先了解一些基本知识:

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

var one = 1;
let two = 2;

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

three = 3;

现在,上述方法之间的区别在于:

var 功能范围

let 是块作用域的。

而不带var/ let关键字声明的变量的范围将变为全局变量,而与声明的位置无关。

可以从网页的任何位置访问全局变量(不建议使用,因为可能会意外修改全局变量)。

现在根据这些概念,让我们看一下所讨论的代码:

 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
}


2
抱歉,我没有这样的意图,因为我们可能从同一来源收集了一些信息,所以可能会有类似的情况。
fatimasajjad

2
可能两个答案都包含从同一原始来源复制的内容而没有明显的引文-可能是tutorialsteacher.com/javascript/javascript-variable。抄袭的存在是显而易见的,因为语法错误是从原始文本复制而来的- “没有var关键字而声明的变量的作用域成为全局变量,而不管它在哪里声明”应该是“范围...成为”“范围...成为“。无论是在这里还是在其他地方,使用别人的确切单词都需要引用。meta.stackexchange.com/q/160071/211183
Michael-sqlbot

谢谢大家,我为源添加了参考链接。
fatimasajjad

6

使用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();


3

这是因为当您不使用时,let或者var变量正在动态声明时,最好像下面这样声明。

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

2

奇怪的问题是由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()将如下所示。在这种情况下,第二个将有权访问所有变量,因为它们在全局范围内

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可以被外部作用域访问,让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


0

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

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

c和b的寿命与有趣的寿命相同,但只有一个块的寿命,如果您尝试通过引用访问a总是显示错误,但是c和b处于全局状态,因此它们不会如此。您会注意到无论有多少您添加到链中的变量,只会是导致错误的第一个(a)。这是因为“ let”将变量的作用域限定于块(或“局部”,或多或少的意思是“放在方括号中”)如果在声明变量时未声明“ let”,则该变量将在全局范围内作用。因此,在设置变量的函数中,所有值都为10(如果在调试器中输入断点)。如果您在第一个功能中放置控制台日志到a,b,c,一切都很好。但是,一旦离开该功能,第一个(a)便会记住,再次提醒您,


0

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

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

在“ second() ” 范围之外,即IE,在全局范围内,“ a ” 的值将如先前所定义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.