首先要知道一段JS程序执行过程中,有哪些角色在起作用:
示例:
var a = 2
这段代码是如何执行的:
编译器在当前作用域声明一个变量a引擎在作用域中查找该变量a,找到了就赋值为2;找不到这个变量a就抛出异常。可以看到,JS代码执行过程都是围绕着作用域的。js代码中的变量都是放在作用域中的。
JS有两种常见作用域:
函数作用域就是一段代码如果用函数进行包裹,那么函数外界就无法访问到这个函数的作用域。
还有一种作用域就是块作用域。如 if(){ .. },for(){ .. },{..} 代码块等这种都是属于块作用域,但是使用var关键字在块作用域中声明的变量,却是可以在这个块作用域外访问到的,因此会有污染全局作用域变量的风险。不过要解决这个问题可以在块作用域中使用let/const关键字来声明变量。
可以将作用域想象成下面这种模型:

这里就包含了三层的作用域:
① 全局作用域,只有一个标识符:foo
② foo的作用域,有三个标识符:a、bar、b
③ bar的作用域,只有一个标识符:c
一个变量的作用域在哪里取决于其在哪里定义的(注意块级作用域中用var声明的变量不属于这个块级作用域,它属于全局作用域)。
引擎在查找某个变量时会由内而外向最外层作用域查找这个变量,如果都没找到就抛出ReferenceError异常。
bar()函数中的console.log(a,b,c)在查找a变量时会先在bar()函数的作用域查找a变量,没找到就去foo()函数的作用域中查找,找到了就拿来用,如果去到了全局作用域中还是没有找到a变量,最终会抛出异常。