咱们知道,变量对于程序来讲是相当重要的,若是没有变量存储和访问值,整个程序会受到限制。那么问题来了,既然程序这么须要变量,那么它究竟是怎么样去存储变量和使用变量的呢?存储变量这里暂且不提,到时候会有专门一篇博客来讲明这个问题。咱们此次说的主要就是如何去使用变量。这就要牵扯到咱们今天的主题做用域上面了。
归纳的来讲,做用域就是一套能让你有序访问变量的规则。(注意有序很重要)javascript中只有函数能封闭做用域(let函数也能绑定一个块级做用域,这里先不作讨论).javascript
来看下面一个例子java
function foo (a) { var b = a * 2 function bar (c) { console.log(a, b, c) } bar(b * 3) console.log(c) } foo(2)
(1)foo函数内部会造成一个做用域
(2)bar函数内部会造成一个做用域
(3)有一个全局的做用域
咱们前面说过,做用域是一套能让你有序访问变量的规则,那么上述代码运行的时候,做用域是怎么样访问变量的呢?来看看下面的示意图。
,一个是函数bar的做用域,一个是函数foo的做用域,一个是全局的做用域。而且这三个做用域是嵌套的。
(1)bar做用域中有一个变量c
(2)foo做用域中有三个变量a,b,bar
(3)全局做用域中有一个变量foo
咱们来看看上面代码的运行过程,首先执行最外层的foo(2),foo在调用栈调用bar,bar执行。可是注意bar内部的执行语句为console.log(a,b,c)
咱们前面已经说过,bar做用域中只有变量c,那么上述语句是否会出现错误呢,答案是不会。上述代码会正常输出。那么为何会这样呢?答案就是代码在运行的过程当中有一个做用域链能做用域给串起来。以下图
内部的做用域能够访问外部做用域的变量。因此bar函数在执行console.log(a,b,c)时,在当前做用域中若是没有找到a,b变量,它会顺着做用域链往上找,在上层做用域foo中找到了a,b变量,它就会使用上层做用域a,b的值。若是上层做用域仍是没有a,b的话,它会顺着做用域继续查找,直到全局变量。若是全局变量仍然没有,程序就会报错。那么既然内部做用域能沿着做用域链访问到外部做用域,那么外部做用域能不能顺着做用域链访问内部做用域呢?不急,继续看下面代码。
执行完bar函数后,bar函数从执行栈中弹出,继续执行foo函数剩余的语句,console.log(c)
因为当前做用域中不存在变量c,可是其子做用域内有变量c的定义,那么程序会不会输出子级做用域的变量c呢?答案是不会。
上级做用域不能经过做用链进入下级做用域。只有下级做用域能经过做用链进入上级做用域。只就是做用域的有序性。有序的访问全部能访问的变量和函数。函数
做用域就像一个一个封闭的空间,不一样做用域内的变量是不会相互影响的。可是做用域之间又会有联系。若是是嵌套的做用域的话,这些嵌套做用域会经过做用域链把嵌套做用域联系在一块儿。内部做用域能经过做用域链访问到上级做用域的变量。即若是当前做用域中没有某个变量,引擎会经过做用域链查找上级做用域看看有没有定义该变量。直到全局做用域。(全局没有则报错)
可是上级做用域无法经过做用域链访问下级做用域。这就是做用域的有序性。经过做用域链能让引擎对执行环境里全部有权访问的变量和函数进行有序访问。spa