JS作用域链讲起来麻烦,本来很好懂的东西,书上讲的很混乱。
先撇开作用域的概念。采用自顶向下的方法来说明,可能会好点。
好,其实全局变量和局部变量大家都懂得。全局变量很容易理解,就是一个内存共享原理。
局部变量是是只属于它的父级(其实就是作用域),也就是说我们必须要找到父级才能找到它。
有没有办法让我们从外部访问局部变量,而不用每次去找它的父级。
比如:
var a = ‘out‘;
var f = function(){
var i = ‘in‘; //var表示声明 i 为局部变量,并定义它的指
} //返回给f的是并不执行的函数
f(); //执行函数,创建局部变量i
//a = i; 这是行不通的,因为 i 是局部变量,外部不可访问
我们知道function() 这个(匿名)函数,一旦执行完,里面的变量或资源我们将不能再获取和使用,除非有办法让里面的资源驻留在我们能够找到存放地的内中。然后我们会想到静态变量,不过JS是没有静态变量的,那我们还是只有用全局变量的方法了么?是,也不是。是,因为我们还可以用闭包,不是,因为闭包也是一种间接的全局变量,不过这种手段让局部变量也有可全局分享的作用。这里的实例用个形象比喻就是,我们用一个绳子(作用域链)栓住了那个局部变量。
var a=‘out‘;
var a_find_in; //声明一个绳子
var f = function(){
var i = ‘in‘;
a_find_in = function(){ //拴住 i 这个变量
return i; //返回这个i的值
};
}
f(); //执行函数,创建局部变量 i
a = a_find_in(); //获取局部变量 i 的值
看到 a_find_in 这个函数相当于一个传递局部变量值的作用,它和 i 绑定在一起就像一个全局变量。只不过a_find_in充当这”全局变量“的地址,i 就是这个”全局变量“的值。有人会说,非得搞一个 a_find_in 出来,不如直接把 i 设置为全局不更简单么。
闭包的作用不单单是为了使用局部变量。你可以看到这里 a_find_in 的作用只是返回给我们 i 的值,我们却不能像全局变量一样修改它。说到这,知道私有变量的话,就明白闭包这个可以让变量隐藏变的安全的作用了。
其次,我们还可以声明和定义一个可以修改局部变量的函数。不过在这之前,还有一个闭包需要特别注意的点要说。
若以上代码,我加上一行如下红色代码:
var a=‘out‘;
var a_find_in; //声明一个绳子
var f = function(){
var i = ‘in‘;
a_find_in = function(){ //拴住 i 这个变量
return i; //返回这个i的值
};
i = ‘change in‘; //让 i 在这个作用域里面改变了值
}
f(); //执行函数,创建局部变量 i
a = a_find_in(); //获取局部变量 i 的值
以上代码 a 会得到 ’change in‘ 的值。这就是闭包的作用。为什么呢,先不解释,再看下面的代码。
var a=‘out‘;
var a_find_in; //声明一个绳子
var f = function(){
var i = ‘in‘;
a_find_in = function(){ //拴住 i 这个变量
return i; //返回这个i的值
}(); //立即执行
i = ‘change in‘; //让 i 在这个作用域里面改变了值
}
f(); //执行函数,创建局部变量 i
a = a_find_in; //获取局部变量 i 的值
以上代码 a 会得到 ‘in‘ 的值,因为函数立即执行,立即返回,a_find_in 在定义的时候加上了 ‘()‘,就相当于绳子一栓到当前的 i 值就立刻收绳,从而也不再和 i 指绑定,失去了闭包关系。就相当于 a_find_in = i; 取了当前 i 的值。
对以上得到 ‘change in‘ 的闭包代码,如要从作用域链的角度去理解,i 就是关键点。function(){return i;} 这个函数赋给了 a_find_in,其实就是把 i 的作用域又链到了 function(){} 这个匿名函数的作用域中,但是 function(){} 现在赋给了外部变量 a_find_in 就相当于有了名字,我们随时可以呼唤(执行)这个函数,从而使用它的作用域,也就可以得到 i 甚至操作 i 的值了。