说起闭包,记忆最深刻的莫过于初中数学老师的解释,一个包含边界的数值范围。一条数轴,两个实心点,一条括号一样的线,表示了闭包。对应的数学记号例如[1,8],包含1、8以及大于1小于8的所有数。网上有些资深人士说,Javascript的闭包就是内部函数,或者更具体点是return的内部函数。作为数学系的毕业生,直觉告诉我应该没有那么简单:
打开Google,输入javascript closure,选择前三个搜索结果,开始研究。
我更愿意把它称为执行环境,感觉这个叫法更接地气,容易理解,下面都以执行环境来称呼它。
所有的代码都需要在某个执行环境里面才能执行,执行环境可以被理解为一个存储key-value的对象。javascript里面常用的有两类执行环境, 全局环境(Global)和函数(Function)环境。每次执行一个函数(Function),当前执行环境就会切换到一个新的执行环境,如果函数里面再调另外函数,会形成执行环境栈(Stack)。全局环境存储两类内容,全局变量和全局的函数。函数环境存储三类内容,参数(Arguments)、局部变量(Local Varibale)、所有子函数。
子函数的执行环境是父函数所形成的函数环境,也就是他能够访问父函数的参数、父函数的局部变量、同级别的其它子函数。执行环境,或者叫可访问范围,才是真正的闭包。函数的执行需要执行环境,函数还存活的时候(比如return回去),它的执行环境不能被垃圾回收器回收,否则函数无法执行。闭包的魔法正在于此。
function func1(){ function func2(){ console.log("Exe func2"); } return function(){ func2(); } } func1()();
func2作为return函数的执行环境,返回之后依然存在。
阮大侠在那篇文章里面提供了两个例子,我尝试用执行环境来解释一下。我的运行环境是Node.js,只是把alert换做console.log而已。代码如下:
var name = "The Window"; var object = { name: "My Object", getNameFunc: function() { return function() { return this.name; }; } }; console.log(object.getNameFunc()()); var name = "The Window"; var object = { name: "My Object", getNameFunc: function() { var that = this; return function() { return that.name; }; } }; console.log(object.getNameFunc()());
执行结果为:
undefined
My Object
我想换一下代码书写方式,但语义不变:
var name = "The Window"; function Object1() { this.name = "My Object"; this.getNameFunc = function() { return function() { return this.name; } } } var obj1 = new Object1(); console.log(obj1.getNameFunc()()); function Object2() { this.name = "My Object"; this.getNameFunc = function() { var that = this; return function() { return that.name; } } } var obj2 = new Object2(); console.log(obj2.getNameFunc()());
区别就是执行环境不同。
推荐英文好的朋友好好看看这篇文章,http://jibbering.com/。长但值得拥有。