首发于:https://mingjiezhang.github.io/
闭包和作用域有着千丝万缕的联系。
js的作用域
具体的作用域我就不展开叙述了。其中很重要的两点就是:js的作用域链机制和函数词法作用域在定义的时候就确定的。对于前者,js中每个函数都有自己的执行环境,执行流进入一个函数时,函数的环境就会被推入一个环境栈,函数执行完之后就把控制权归还,这也是垃圾回收机制的原理,当代码在一个环境中执行会创建变量对象一个作用域链,标识符解析是沿着作用域链一级一级搜索的。对于后者,有些人认为函数词法作用域是执行时确定的,但这是错误的,本身词法作用域的定义就于此违背,我们可以通过下面这个例子来证明。
function b(){
var x=3;
a();
}
function a(){
console.log(x);
}
b();//x is not defined.
如果函数作用域是执行时候确定的,那么按理结果应该是打印3。但是我们看到是x未定义,这完美地证明了a()的作用域链中是不存在b()的局部作用域。因此,我们可以确定函数词法作用域在定义的时候就确定的。
作个小总结:函数作用域是在函数定义的时候就确定了的,但是只有函数作用域需要通过执行函数来激活函数局部作用域,如果未执行函数或者函数执行完毕,该函数的变量对象的作用域都是不能被使用的或者被回收的,这点对于理解闭包十分重要。
初探闭包
闭包的定义有很多,在《You dont'konw JavaScript》中闭包的定义:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
我们来解读下这句话,闭包怎么产生,就是一个内部函数可以记住它所在的作用域的时候,这个函数就是个闭包了,而且,这个函数在这个作用域外执行的时候,也是可以记住该作用域的。
我们先通过一个demo来解析这个定义。
function out(){
var a=1;
function in(){
console.log(a);
}
return in;
}//定义out函数
var temp=out();//将out函数执行返回的in函数引用赋值给temp变量
temp();//结果输出1
假如没有闭包:按照js的垃圾回收机制,在out()函数执行完毕的时候,out的内部作用域应该被销毁,也就是这时候变量a本应该销毁,这时候函数temp()中无法获得a声明所在的作用域。
不过闭包却可以改变这一切,out函数在执行完毕后,out内部作用域仍然存在。这时候,temp()函数执行的时候,可以记住并访问out()内部的作用域,这时候temp()函数可以访问a。
这就是闭包的神奇之处,虽然外部函数执行完毕,但是内部函数如果有变量引用外部函数作用域中的变量,那么该外部函数的变量对象就不会被回收,可以继续被使用。
闭包的意义和危险
闭包的意义就是可以让一个外部函数中的内部函数在非当前作用域中被使用,同时这个内部函数可以操作外部函数的作用域中的变量。模块的实现与闭包有很联系。具体的例子这边不展开叙述。
当然闭包也很危险,因为外部函数执行完毕后,外部函数的作用域的变量对象一直被引用,长期未被回收会造成内存泄漏,因此我们需要手动去销毁这些被引用的变量。
欢迎指正交流。未经允许,请勿转载。