参考自:深入理解javascript原型和闭包系列(王福朋 - 博客园)。如有侵权,请联系删除。
自由变量 和 作用域链
先解释一下什么是“自由变量”。
在A作用域中使用的变量 x,却没有在 A 作用域中声明(即在其他作用域中声明的),对于 A 作用域来说,x就是一个自由变量:
var a = 10; |
在上面的程序中,在调用 fn() 函数时,函数体中第 6 行。取 b 的值就直接可以在 fn 作用域中取,因为 b 就是在这里定义的。而取 a 的值时,就需要到另一个作用域中取。
问题在于去哪取?有人说过要到父作用域中取,其实有时候这种解释会产生歧义。实际上准确的说法应该是:要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”——其实这就是所谓的“静态作用域”。例如:
var x = 10; |
在这段代码中,fn 函数取自由变量 x 的值时,要到哪个作用域中取?——要到创建 fn 函数的那个作用域中取——无论 fn 函数将在哪里调用。
var a = 10; |
第二例也一样,bar 函数中 a 和 b 都是自由变量,其中 b 在创建 bar 的 fn 作用域中找到了,值为 40(因为 return bar
时 b 已经执行了 += 20
,若在 += 20
前调用 bar();
,b 的值为20);而 a 在 fn 作用域中仍找不到,故再在创建 fn 函数的作用域,即全局作用域中找到 a。
上下文环境 和 作用域 的关系
上下文环境
可以理解为一个看不见摸不着的对象(有若干个属性),虽然看不见摸不着,但确实实实在在存在的,因为所有的变量都在里面存储着,要不然咱们定义的变量在哪里存?
另外,对于函数来说,上下文环境是在调用时创建的,这个很好理解。拿参数做例子,你不调用函数,我哪儿知道你要给我传什么参数?
作用域
首先,它很抽象。第二,记住一句话:除了全局作用域,只有函数才能创建作用域。创建一个函数就创建了一个作用域,无论你调用不调用,函数只要创建了,它就有独立的作用域,就有自己的一个“地盘”。
两者
一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。
最后引用一张图片来结尾吧:
