本文收集了多本书里对
JavaScript闭包(Closure)
的解释,或许会对理解闭包有必定帮助。编程
JavaScript 中闭包无处不在,你只须要可以识别并拥抱它。设计模式
闭包是基于词法做用域书写代码时所产生的天然结果。闭包
当函数能够记住并访问所在的词法做用域时,就产生了闭包,即便函数是在当前词法做用域以外执行。异步
不管经过何种手段将内部函数传递到所在的词法做用域之外,它都会持有对原始定义做用域的引用,不管在何处执行这个函数都会使用闭包。函数
不管什么时候何地,若是将函数(访问它们各自的词法做用域)看成第一级的值类型并处处传递,你就会看到闭包在这些函数中的应用。在定时器、事件监听器、 Ajax 请求、跨窗口通讯、Web Workers 或者任何其它的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包。this
var fn = f(); // 将函数f 的返回值赋值给变量fn fn(); // 1 fn(); //2 fn(); //3 function f() { var cnt = 0; return function() { return ++cnt; } } var fn1 = f1(); fn1(); //1 fn1(); //1 function f1(){ var cnt = 0; return ++cnt; }
从表面上来看,闭包是一种具备状态的函数。或者也能够将闭包的特征理解为,其相关的局部变量在函数调用结束以后将会继续存在。设计
闭包的前提条件是须要在函数声明的内部声明另外一个函数(即嵌套的函数声明)。code
闭包指的是一种特殊的函数,这种函数会在被调用时保持当时的变量名查找的执行环境。对象
闭包仅仅是保持了变量名查找的状态,而并无保持对象全部的状态,对此请加以区分。也就是说,闭包虽然会保持(在嵌套外层进行函数调用时被隐式地生成的)Call 对象,但没法保持 Call 对象的属性所引用的以前的对象的状态。事件
模块
避免使用全局变量
经过实现信息隐藏
//使用了闭包的模块 // 在此调用匿名函数 // 因为匿名函数的返回值是一个函数,因此变量sum 是一个函数 var sum = (function() { // 没法从函数外部访问该名称 // 实际上,这变成了一个私有变量 // 通常来讲,在函数被调用以后该名称就将没法再被访问 // 不过因为是在被返回的匿名函数中,因此仍能够继续被使用 var position = { x:2, y:3 }; // 一样是一个从函数外部没法被访问的私有变量 // 将其命名为sum 也能够。不过为了不混淆,这里采用其余名称 function sum_internal(a, b) { return Number(a) + Number(b); } // 只不过是为了使用上面的两个名称而随意设计的返回值 return function(a, b) { print('x = ', position.x); return sum_internal(a, b); }; } )(); // 调用 sum(3, 4); x = 2 7
在利用函数做用域能够封装名称,以及闭包可使名称在函数调用结束后依然存在这两个特性后,信息隐藏得以实现。
(function() { 函数体 })();
计数器功能的类
function counter_class(init) { // 初始值能够经过参数设定 var cnt = init || 0; // 设置默认参数的习惯作法(参见5.5 节) // 若有必要,可在此声明私有变量与私有函数 return { // 公有方法 show:function() { print(cnt); }, up:function() { cnt++; return this; }, // return this 在使用方法链时很方便 down:function() { cnt--; return this; } }; } // 使用代码 var counter1 = counter_class(); counter1.show(); 0 counter1.up(); counter1.show(); 1 var counter2 = counter_class(10); counter2.up().up().up().show(); // 方法链 13
表达式闭包
JavaScript 有一种自带的加强功能,称为支持函数型程序设计的表达式闭包(Expression closure)。
var sum = function(a, b) { return Number(a) + Number(b); } //能够省略为 var sum = function(a, b) Number(a) + Number(b);
闭包是指有权访问另外一个函数做用域中的变量的函数。建立闭包的常见方式,就是在一个函数内部建立另外一个函数
当某个函数被调用时,会建立一个执行环境(execution context)及相应的做用域链。
因为闭包会携带包含它的函数的做用域,所以会比其余函数占用更多的内存。过分使用闭包可能会致使内存占用过多,咱们建议读者只在绝对必要时再考虑使用闭包。
因为IE9以前的版本对JScript对象和COM对象使用不一样的垃圾收集例程,所以闭包在IE的这些版本中会致使一些特殊的问题。具体来讲,若是闭包的做用域链中保存着一个HTML元素,那么就意味着该元素将没法被销毁。
function assignHandler(){ var element = document.getElementById("someElement"); element.onclick = function(){ alert(element.id); }; } //把element变量设置为null。这样就可以解除对DOM对象的引用,顺利地减小其引用数,确保正常回收其占用的内存。 function assignHandler(){ var element = document.getElementById("someElement"); var id = element.id; element.onclick = function(){ alert(id); }; element = null; }
封闭变量
延续局部变量的寿命
//把img变量用闭包封闭起来,便能解决请求丢失的问题 var report = (function(){ var imgs = []; return function( src ){ var img = new Image(); imgs.push( img ); img.src = src; } })();
局部变量原本应该在函数退出的时候被解除引用,但若是局部变量被封闭在闭包造成的环境中,那么这个局部变量就能一直生存下去。若是在未来须要回收这些变量,咱们能够手动把这些变量设为null。
跟闭包和内存泄露有关系的地方是,使用闭包的同时比较容易造成循环引用,若是闭包的做用域链中保存着一些DOM节点,这时候就有可能形成内存泄露。但这自己并不是闭包的问题,也并不是JavaScript的问题。
若是要解决循环引用带来的内存泄露问题,咱们只须要把循环引用中的变量设为null便可。将变量设置为null意味着切断变量与它此前引用的值之间的链接。当垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存。
JavaScript也采用词法做用域(lexical scoping),也就是说,函数的执行依赖于变量做用域,这个做用域是在函数定义时决定的,而不是函数调用时决定的。为了实现这种词法做用域,JavaScript函数对象的内部状态不只包含函数的代码逻辑,还必须引用当前的做用域链。函数对象能够经过做用域链相互关联起来,函数体内部的变量均可以保存在函数做用域内,这种特性在计算机科学文献中称为“闭包”。
从技术的角度讲,全部的JavaScript函数都是闭包:它们都是对象,它们都关联到做用域链。
是若是这个函数定义了嵌套的函数,并将它做为返回值返回或者存储在某处的属性里,这时就会有一个外部引用指向这个嵌套的函数。它就不会被当作垃圾回收,而且它所指向的变量绑定对象也不会被当作垃圾回收。