作用域与闭包
什么是作用域?作用域(scope)指的是变量存在的范围,这里只讨论 ES 6 之前的全局作用域与函数作用域;
全局变量(global)是在函数外部,比如浏览器的 window,在这个地方声明的变量就是全局变量,全局变量在全局作用域下声明,又可以在函数作用域中使用;
函数作用域(local)是在函数内部,但我们声明了一个函数的时候,就产生了一个 local 作用域,在其中声明的变量,我们就叫局部变量,局部变量不能在外层使用,只能在自己本层作用域,或者下一层作用域中使用。
// global
function f1() {
// local_1
function f2() {
// local_2
function f3() {
// local_3
}
}
function f4() {
// local_4
}
}

作用域规则一:正常情况下,外层作用域的变量能在内层中用,但是内层的不能被外层使用。
比如图中的 local_3 中的变量,对于 global, local_1, local_2, local_4 来说都是不存在的。
作用域规则二:变量的作用域取决于它声明时的位置。
举个例子
var a = 1;
function f1(){
var a = 2;
f2.call();
}
function f2() {
console.log(a);
}
f1.call();
// → 1
例子中,我们在全局声明了一个函数 f2, 然后在 f1 中调用它,然而它的作用域依然取决于它最初声明的位置,所以最终打印了 1。
上面的例子是,我们把外面的函数放到了内层的作用域,那么,反过来,我们把内层的的变量、函数拿到外层来,这个变量、函数所在的作用域依然是内部原来的位置。
有没有可能实现呢?有没有必要呢?
实现是没问题的:
var a = 1;
function f1() {
var a = 2;
var f2 = function() {
console.log(a)
}
return f2;
}
var f = f1.call(); // f2
f.call() // → 2;
如上,但我们在 f1 中把 f2 return 出来,我们在全局作用域就得到了一个 f2,只不过这个函数的作用域依然是原来的,调用打印 2。
这就是我们常说的闭包(closure),也是我们最常常看到的它的样子。
关于闭包,方老师的文章中有一个简洁朴素的定义:
「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。
也就是说,闭包就是一个函数(f2)加上它能够用的变量(例子中的 var a = 2),因为作用域的作用规则,我们可以通过函数访问闭包中的变量。
于是,闭包的作用在于,隐藏变量。
关于这个隐藏到底意义多大,我暂时没有太深刻的体会,不过我们可以做个简单的对比。
变量放在全局中,可以被任意操作;
而如果放到闭包中,只能进行我们原型设定好的操作方式。