关于js作用域的一个小问题,实在搞不明白了
发布于 9 年前 作者 clm1100 5141 次浏览 最后一次编辑是 8 年前 来自 问答
<!DOCTYPE>
<html>
 <head>
  <title> New Document </title>
 </head>

 <body>
  <script>
  function foo(){var a=2;bar()};
  function bar(){console.log(a)};
  foo()
  </script>
 </body>
</html>

js这段代码会报 这个错误 Uncaught ReferenceError: a is not defined, 解决方法到是有很多,但是不明白为什么会报错; 请各位达人们,给出一个比较透彻的解释

20 回复

b函数定义在全局作用域里面 它查找a时会在全局作用域里面查找,找不到自然就报错了。。。

a在bar里面没定义,在它的闭包中也没定义。所以出错。 你理解成把bar定义在foo中了。如果定义在foo中,a在bar的闭包中有定义,就能执行。

我面试官跟我讲函数也是一个闭包!

其实可以反过来想, 在 a 函数里定义的变量,如果在 b 函数里也能访问,那不就乱套啦? 意味着变量都是全局的… 各种冲突…

JS采用词法作用域。

也就是说这个作用域是在函数定义时决定的,而不是函数调用时决定的。

@ncuzp 他没说错,在技术的角度,所有函数都是一个闭包。

但是改成这样的话,确是可以运行,就是把bar函数放到foo的作用域里面去声明

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
  <title> New Document </title>
  <meta name="Generator" content="EditPlus">
  <meta name="Author" content="">
  <meta name="Keywords" content="">
  <meta name="Description" content="">
 </head>

 <body>
  <script>
  
  function foo(){
  var a=2;
  function bar(){console.log(a)}
  bar();
  };
  
  foo()
  </script>
 </body>
</html>

@elrrrrrrr 但是通过闭包就可以访问到啊,代码如下

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
  <title> New Document </title>
  <meta name="Generator" content="EditPlus">
  <meta name="Author" content="">
  <meta name="Keywords" content="">
  <meta name="Description" content="">
 </head>

 <body>
  <script>
  
  function foo(){
  var a=2;
  return function(){
  console.log(a)
  }
  };
  
  foo()()
  </script>
 </body>
</html>

@ayiis 还是有点不太明白,bar函数作用域该怎么理解呢,bar函数在foo函数的作用域里面运行的时候作用是否发生了变化呢;

es5中作用域有两个。全局作用域和函数作用域。在打印a的方法中没有定义a所以报错。。

简单点说就是函数的作用域链是顺着创建这个函数的上级作用域来查找的。。bar虽然在foo中使用了,但是他是在全局作用域中创建的,全局作用域中没有a,所以报未定义。

@eyblog 正解 按照你这个理解就说的通了,感谢感谢

@anotherWill 我把代码改一下,把bar函数声明改到foo函数里面, 注意这会儿,#在打印a的方法中没有定义a(不知道我这句话有没有理解错误); 看代码;

<!DOCTYPE>
<html>
 <head>
  <title> New Document </title>
 </head>
 <body>
  <script>
  function foo(){
  var a = 10;
  function bar(){
	console.log(a)
  }
  bar()
  };
  foo()
  </script>
 </body>
</html>

此时是可以打印的

@eyblog 刚才突发奇想,按照我理解的你的思路,bar()是访问不到,foo里面 var a=10 的; 所以把代码小小改动了一下,这下又出现个困惑了, 万望解答,代码如下:在全局定义一个a;然后在foo函数里面给a赋值;此时bar可以访问到a

<!DOCTYPE>
<html>
 <head>
  <title> New Document </title>
 </head>
 <body>
  <script>
  var a;
  function foo(){
  a=1000;
  bar()
  };
  function bar(){
	console.log(a)
  }
  foo()
  </script>
 </body>
</html>

@clm1100 按ES5说,js只有函数作用域,也就是函数可直接访问其外面定义的变量,外面不能直接访问函数内定义的变量;即使闭包也不违背这句话。

var a;//函数外,相当于window.a,对foo和bar均可见
function foo(){
  a = 1000;//即window.a = 1000;
  bar();
}
function bar(){
  console.log(a);//即 console.log(window.a)
}
foo();//根据变量可见范围去追踪理解每一步执行的过程

@clm1100 这个是最基本的知识啊,函数内定义变量不加var 的话也会顺着作用域链往上找,所以你这个在foo里a=1000,相当于对全局作用域a的赋值,所以bar里面会访问到a=1000.

我是这样理解的: 在浏览器中,最大的域就是window,然后再就是对象域、函数域。如果一个函数没有归属于一个对象域,那么他就处于window域中。 function foo(){var a=2;bar()}; function bar(){console.log(a)}; foo(); 如上所示代码,foo函数与bar函数都是属于window域。var a = 2;表示a变量属于foo这个域,当你调用bar时,会先找bar域,然后再找window域。这时你是找不到a变量的。所以报错拉。

回到顶部