JavaScript中的闭包(Closure)详细介绍


Posted in Javascript onDecember 30, 2014

闭包是JavaScript中一个重要的特性,其最大的作用在于保存函数运行过程中的信息。在JavaScript中,闭包的诸多特性源自函数调用过程中的作用域链上。

 

函数调用对象与变量的作用域链

 

对于JavaScript中的每一次函数调用,JavaScript都会创建一个局部对象以储存在该函数中定义的局部变量;如果在该函数内部还有一个嵌套定义的函数(nested function),那么JavaScript会在已经定义的局部对象之上再定义一个嵌套局部对象。对于一个函数,其内部有多少层的嵌套函数定义,也就有多少层的嵌套局部对象。该局部对象称为“函数调用对象”(ECMAScript 3中的“call object”,ECMAScript 5中改名为“declarative environment record”,但个人认为还是ECMAScript 3中的名称更容易理解一些)。以下面的函数调用为例:

function f(x){

  var a = 10;

  return a*x;

}

console.log(f(6));//60

在这个简单的例子中,当调用f()函数时,JavaScript会创建一个f()函数的调用对象(姑且称之为f_invokeObj),在f_invokeObj对象内部有两个属性:a和x;运行f()时,a值为10而x值为6,因此最后的返回结果为60。图示如下:

JavaScript中的闭包(Closure)详细介绍

当存在函数嵌套时,JavaScript将创建多个函数调用对象:

function f(x){

  var a = 10;

  return a*g(x);

  function g(b){

    return b*b;

  }

}

console.log(f(6));//360

在这个例子中,当调用f()函数时,JavaScript会创建一个f()函数的调用对象(f_invokeObj),其内部有两个属性a和x,a值为10而x值为6;运行f()时,JavaScript会对f()函数中的g()函数进行解析定义,并创建g()的调用对象(g_invokeObj),其内部有一个属性b,b值与传入参数x相同为6,因此最后的返回结果为360。图示如下:

JavaScript中的闭包(Closure)详细介绍

可以看到,函数调用对象形成了一条链。当内嵌函数g()运行,需要获取变量值的时候,会从最近的函数调用对象中开始进行搜索,如果无法搜索到,则沿函数调用对象链在更远的调用对象中进行搜寻,此即所谓的“变量的作用域链”。如果两个函数调用对象中出现相同的变量,则函数会取离自己最近的那个调用对象中的变量值:

function f(x){

  var a = 10;

  return a*g(x);

  function g(b){

    var a = 1;

    return b*b*a;

  }

}

console.log(f(6));//360, not 3600

在上面的例子中,g()函数的调用对象(g_invokeObj)和f()函数的调用对象(f_invokeObj)中均存在变量a且a的值不同,当运行g()函数时,在g()函数内部所使用的a值为1,而在g()函数外部所使用的a值则为10。图示此时的函数调用对象链如下:

JavaScript中的闭包(Closure)详细介绍

什么是闭包?

在JavaScript中所有的函数(function)都是对象,而定义函数时都会产生相应的函数调用对象链,一次函数定义对应一个函数调用对象链。只要函数对象存在,相应的函数调用对象就存在;一旦某函数不再被使用,相应的函数调用对象就会被垃圾回收掉;而这种函数对象和函数调用对象链之间的一一组合,就称之为“闭包”。在上面f()函数和g()函数的例子中,就存在两个闭包:f()函数对象和f_invokeObj对象组成了一个闭包,而g()函数对象和g_invokeObj-f_invokeObj对象链一起组成了第二个闭包。当g()函数执行完毕后,由于g()函数不再被使用,因此g()闭包被垃圾回收了;之后,当f()函数执行完毕后,由于同样的原因,f()闭包也被垃圾回收了。

从闭包的定义可以得出结论:所有的JavaScript函数在定义后都是闭包 ? 因为所有的函数都是对象,所有的函数在执行后也都有其对应的调用对象链。

不过,令闭包真正发挥作用的是嵌套函数的情况。由于内嵌函数是在外部函数运行的时候才开始定义的,因此内嵌函数的闭包中所保存的变量值(尤其是外部函数的局部变量值)是这次运行过程中的值。只要内嵌函数对象依然存在,那么其闭包就依然存在(闭包中的变量值不会发生任何改变),从而也就实现了保存函数运行过程的信息这个目的。考虑以下这个例子:

var a = "outside";

function f(){

  var a = "inside";

  function g(){return a;}

  return g;

}

var result = f();

console.log(result());//inside

在这个例子中,当运行f()函数时,g()函数被定义,同时创建了g()函数的闭包,g()闭包包含了g_invokeObj-f_invokeObj对象链,因此保存了f()函数执行过程中的变量a的值。当执行console.log()语句时,由于g函数对象仍然存在,因此g()闭包也依然存在;当运行这个仍然存在的g函数对象时,JavaScript会使用依然存在的g()闭包并从中获取变量a的值(“inside”)。

Javascript 相关文章推荐
js 代码集(学习js的朋友可以看下)
Jul 22 Javascript
JavaScript 利用StringBuffer类提升+=拼接字符串效率
Nov 24 Javascript
js substr支持中文截取函数代码(中文是双字节)
Apr 17 Javascript
JavaScript数组深拷贝和浅拷贝的两种方法
Apr 16 Javascript
IE6浏览器中window.location.href无效的解决方法
Nov 20 Javascript
angularJS结合canvas画图例子
Feb 09 Javascript
JavaScript中通过提示框跳转页面的方法
Feb 14 Javascript
AngularJS使用带属性值的ng-app指令实现自定义模块自动加载的方法
Jan 04 Javascript
vue v-model表单控件绑定详解
May 17 Javascript
es6在react中的应用代码解析
Nov 08 Javascript
vue-element-admin 菜单标签失效的解决方式
Nov 12 Javascript
JavaScript实现拖拽盒子效果
Feb 06 Javascript
JavaScript中的类(Class)详细介绍
Dec 30 #Javascript
JavaScript实现防止网页被嵌入Frame框架的代码分享
Dec 29 #Javascript
jQuery实现ichat在线客服插件
Dec 29 #Javascript
jQuery中用dom操作替代正则表达式
Dec 29 #Javascript
jQuery中:animated选择器用法实例
Dec 29 #Javascript
纯JavaScript实现获取onclick、onchange等事件的值
Dec 29 #Javascript
JavaScript实现列出数组中最长的连续数
Dec 29 #Javascript
You might like
木翼下载系统中说明的PHP安全配置方法
2007/06/16 PHP
解析PHP实现下载文件的两种方法
2013/07/05 PHP
PHP5.5迭代生成器用法实例详解
2016/03/16 PHP
PHP微信公众号自动发送红包API
2016/06/01 PHP
PHP中的日期时间处理利器实例(Carbon)
2017/06/09 PHP
Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示例
2019/10/12 PHP
使用JavaScript检测Firefox浏览器是否启用了Firebug的代码
2010/12/28 Javascript
jQuery插件分享之分页插件jqPagination
2014/06/06 Javascript
js实现透明度渐变效果的方法
2015/04/10 Javascript
纯JavaScript实现的分页插件实例
2015/07/14 Javascript
使用JQuery FancyBox插件实现图片展示特效
2015/11/16 Javascript
jQuery控制li上下循环滚动插件用法实例(附demo源码下载)
2016/05/28 Javascript
jQuery实现可拖拽的许愿墙效果【附demo源码下载】
2016/09/14 Javascript
vue实现组件之间传值功能示例
2018/07/13 Javascript
opencv 识别微信登录验证滑动块位置
2018/08/07 Javascript
jQuery实现的页面弹幕效果【测试可用】
2018/08/17 jQuery
ES6之Proxy的get方法详解
2019/10/11 Javascript
vue+导航锚点联动-滚动监听和点击平滑滚动跳转实例
2019/11/13 Javascript
[42:52]Optic vs Serenity 2018国际邀请赛淘汰赛BO3 第二场 8.22
2018/08/23 DOTA
Python中的异常处理学习笔记
2015/01/28 Python
在Python程序和Flask框架中使用SQLAlchemy的教程
2016/06/06 Python
Python实现判断一行代码是否为注释的方法
2018/05/23 Python
python 实现对数据集的归一化的方法(0-1之间)
2018/07/17 Python
python将字符串以utf-8格式保存在txt文件中的方法
2018/10/30 Python
Python实现查找二叉搜索树第k大的节点功能示例
2019/01/24 Python
html5 div布局与table布局详解
2016/11/16 HTML / CSS
英国派对礼服和连衣裙购物网站:TFNC London
2018/07/07 全球购物
乌克兰品牌化妆品和香水在线商店:Bomond
2020/01/14 全球购物
《乡愁》教学反思
2014/02/18 职场文书
毕业生自荐信格式
2014/03/07 职场文书
初中生旷课检讨书范文
2014/10/06 职场文书
技术股东合作协议书
2014/12/02 职场文书
2015年护士节活动总结
2015/02/10 职场文书
文案策划岗位职责
2015/02/11 职场文书
护理专业毕业自我鉴定
2019/08/12 职场文书
使用numpy实现矩阵的翻转(flip)与旋转
2021/06/03 Python