变量作用域和执行上下文(下)

JavaScript基础知识系列

作者 Trevor Cui 日期 2018-02-23
变量作用域和执行上下文(下)

参考自:深入理解javascript原型和闭包系列(王福朋 - 博客园)。如有侵权,请联系删除。

自由变量 和 作用域链

先解释一下什么是“自由变量”。

在A作用域中使用的变量 x,却没有在 A 作用域中声明(即在其他作用域中声明的),对于 A 作用域来说,x就是一个自由变量:

var a = 10;
function fn() {
var b = 20;
console.log(a + b); // 这里 a 就是一个自由变量
}

在上面的程序中,在调用 fn() 函数时,函数体中第 6 行。取 b 的值就直接可以在 fn 作用域中取,因为 b 就是在这里定义的。而取 a 的值时,就需要到另一个作用域中取。

问题在于去哪取?有人说过要到父作用域中取,其实有时候这种解释会产生歧义。实际上准确的说法应该是:要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”——其实这就是所谓的“静态作用域”。例如:

var x = 10;

function fn() { // 函数创建的地方
console.log(x);
}

function show(f) {
var x = 20;
(function () {
f(); // 打印10,而不是20
})();
}

show(fn);

在这段代码中,fn 函数取自由变量 x 的值时,要到哪个作用域中取?——要到创建 fn 函数的那个作用域中取——无论 fn 函数将在哪里调用。

var a = 10;

function fn() {
var b = 20;
function bar() {
console.log(a + b); // a = 10, b = 40, 故打印 50
}
b += 20;
return bar;
}

var x = fn(), b = 200;
x();

第二例也一样,bar 函数中 a 和 b 都是自由变量,其中 b 在创建 bar 的 fn 作用域中找到了,值为 40(因为 return bar 时 b 已经执行了 += 20,若在 += 20 前调用 bar();,b 的值为20);而 a 在 fn 作用域中仍找不到,故再在创建 fn 函数的作用域,即全局作用域中找到 a。

上下文环境 和 作用域 的关系

上下文环境

可以理解为一个看不见摸不着的对象(有若干个属性),虽然看不见摸不着,但确实实实在在存在的,因为所有的变量都在里面存储着,要不然咱们定义的变量在哪里存?

另外,对于函数来说,上下文环境是在调用时创建的,这个很好理解。拿参数做例子,你不调用函数,我哪儿知道你要给我传什么参数?

作用域

首先,它很抽象。第二,记住一句话:除了全局作用域,只有函数才能创建作用域。创建一个函数就创建了一个作用域,无论你调用不调用,函数只要创建了,它就有独立的作用域,就有自己的一个“地盘”。

两者

一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。

最后引用一张图片来结尾吧: