js中var变量的问题?

代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      a = 1;
      function test() {
        console.log(a); // undefined
        var a = 2;
        console.log(a); // 2
      }
    </script>
    <button onclick="test()">test</button>
  </body>
</html>

为什么第一个log打印是 undefined 而不是 1?那个全局的 a 应该在这里生效的才对啊

阅读 930
avatarAI BotBETA

这是因为 JavaScript 中存在变量提升(Hoisting)的概念。当你在函数或全局范围内声明变量时,JavaScript 会将变量的声明提升到它们所在的作用域的顶部。这意味着,即使你在后面才给变量赋值,JavaScript 也会在程序开始时将变量声明提升到顶部。

在你的代码中,你在函数 test 内声明了一个局部变量 a。这使得在函数内部,a 的值只会是局部变量 a 的值,而不是全局变量 a 的值。尽管你在函数内部先打印了 a,然后再声明了局部变量 a,但由于变量提升,JavaScript 会先找到局部变量 a 的声明,并将其提升到函数的顶部。因此,当你打印 a 时,它还是 undefined,因为此时还没有给局部变量 a 赋值。只有在你执行 var a = 2; 时,局部变量 a 才被赋值,此时它的值才变成 2

如果你希望在函数 test 内使用全局变量 a 的值,你可以直接引用全局变量 a,而不需要再声明一个局部变量 a。例如:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      a = 1;
      function test() {
        console.log(a); // 1
        a = 2;
        console.log(a); // 2
      }
    </script>
    <button onclick="test()">test</button>
  </body>
</html>
6 个回答

你问题的中的代码可以等价于下面的:

a = 1;
function test() {
  var a;
  console.log(a); // undefined
  a = 2;
  console.log(a); // 2
}

var a = 2; 语句包含了声明变量和给变量赋值,因为变量提升(hositing)会把变量声明提升到函数开始位置,而赋值语句不会提升。所以运行到第一个console.log(a); 时,因为函数已经有 a 变量,就会输出 函数中 a 变量的值,因为只有声明没有初始化,所以是 undefined.

首先声明,是GPT-3.5给的答案哈。。。。

在你的代码中,虽然你在<script>标签中声明了全局变量a = 1;,但是在函数test()内部的第一次console.log(a);却输出的是undefined而不是1。这是因为在JavaScript中,变量声明会被提升(hoisting),而初始化则不会。

在函数内部,JavaScript将var a = 2;的声明提升到了函数的顶部,但它的初始化部分却保留在原来的位置。这就导致了在console.log(a);之前,a已经被声明了,但它的值还没有被赋予,因此默认为undefined

所以,实际的执行顺序是:

  1. 在函数test()内部,变量a被声明,并被提升到了函数顶部。
  2. 第一个console.log(a);打印的是提升后的a,此时还没有赋值,所以为undefined
  3. 然后,var a = 2;语句将a赋值为2
  4. 第二个console.log(a);打印的是已经赋值的a,所以输出2

如果你想让第一个console.log(a);输出全局的a的值1,你可以在函数内明确指定使用全局作用域的变量,而不是在函数内声明一个新的a。可以这样修改代码:

function test() {
  console.log(window.a); // 1
  var a = 2;
  console.log(a); // 2
}

在这里,window.a明确指定了全局作用域的变量,所以它输出的是全局变量a的值。

题主提出这个问题,说明JS的基础需要巩固和系统学习

作用域

首先,在js中的作用域分为全局作用域、函数作用域和块级作用域。在之前js只存在全局作用域和函数作用域,ES6之后引入了块级作用域。而块级作用域只适用于constlet声明的变量,所以这里我们不需要考虑块级作用域。

// 这里是全局作用域
function test() {
  // 这里是函数作用域
}

js中变量的查找是从当前的函数作用域开始向上查找的,也就是说如果在test中使用变量a,那么会先在test的函数作用域查找,如果在test中存在变量a则停止查找。

变量提升

jsvar声明的变量会被提前到作用域顶部,为什么会这样?

要回答这个问题,需要了解JS是如何被运行的。Java Script引擎在执行JavaScript代码时,需要用到编译器、执行引擎和作用域。

● 编译器:语法分析、代码生成等功能。
● 执行引擎:从头到尾负责整个程序的编译及执行过程。
● 作用域:负责收集并维护所有标识符组成的一系列查询,并确定当前执行代码对这些标识符的访问权限。

执行流程如下:

image.png

js会在编译阶段,创建作用域。也就是var a的空间声明,而编译阶段又先于执行阶段。所以表现为变量提升,也就是传说中的:

var a = 2;

被理解为

var a;
a = 2;

问题分析

我们再回头看你的问题:

a = 1;
function test() {
  console.log(a); // undefined
  var a = 2;
  console.log(a); // 2
}

代码中存在两个作用域:一个全局作用域和一个函数作用域。在全局作用域中a的值是1,这里很好理解吧!

在函数作用域中存在变量声明var a,那么在编译时会先在该函数作用域创建出a的空间,接着在执行test()时,先执行console.log(a),这时先在当前函数作用域中查找变量a。由于编译阶段已经声明了a这个变量,所以这时候在当前函数作用域找到了a,但是这时候还没执行a = 2的命令,此时aundefined

为了更好理解,我这里有一张图,可以帮助你理解:

var a = 2;

image.png

JavaScript中变量会被提升到作用域的顶部 但是赋值的初始化操作会留在原地

1)这是作用域的问题。
2)方法执行,会先行在函数体里找变量,没有再向上。
3)在这里,能找到,但在下面,所以执行时是undefined

推荐问题
宣传栏