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 相关文章推荐
jQuery 表单验证扩展(三)
Oct 20 Javascript
JS 控制小数位数的实现代码
Aug 02 Javascript
firebug的一个有趣现象介绍
Nov 30 Javascript
浏览器加载、渲染和解析过程黑箱简析
Nov 29 Javascript
JavaScript制作windows经典扫雷小游戏
Mar 31 Javascript
jquery实现可关闭的倒计时广告特效代码
Sep 02 Javascript
bootstrap-treeview自定义双击事件实现方法
Jan 09 Javascript
使用yeoman构建angular应用的方法
Aug 14 Javascript
浅谈webpack打包过程中因为图片的路径导致的问题
Feb 21 Javascript
vue.js 2.0实现简单分页效果
Jul 29 Javascript
详解datagrid使用方法(重要)
Nov 06 Javascript
JavaScript继承的三种方法实例
May 12 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 REMOTE_ADDR之获取访客IP的代码
2008/04/22 PHP
PHP文章采集URL补全函数(FormatUrl)
2012/08/02 PHP
PHP中判断变量为空的几种方法小结
2013/11/12 PHP
JavaScript 编程引入命名空间的方法与代码
2007/08/13 Javascript
jQuery的写法不同导致的兼容性问题的解决方法
2010/07/29 Javascript
jQuery+ajax中getJSON() 用法实例
2014/12/22 Javascript
jQuery实现放大镜效果实例代码
2016/03/17 Javascript
深入浅析JavaScript中的constructor
2016/04/19 Javascript
Vue服务端渲染和Vue浏览器端渲染的性能对比(实例PK )
2017/03/31 Javascript
AngularJS获取json数据的方法详解
2017/05/27 Javascript
vue二级路由设置方法
2018/02/09 Javascript
使用async await 封装 axios的方法
2018/07/09 Javascript
JS实现随机抽选获奖者
2019/11/07 Javascript
JQuery中的常用事件、对象属性与使用方法分析
2019/12/23 jQuery
[01:31:22]Ti4 循环赛第四日附加赛LGD vs Mouz
2014/07/13 DOTA
Python数据类型详解(三)元祖:tuple
2016/05/08 Python
Python中多个数组行合并及列合并的方法总结
2018/04/12 Python
解决pycharm无法调用pip安装的包问题
2018/05/18 Python
Django基础知识与基本应用入门教程
2018/07/20 Python
Python入门Anaconda和Pycharm的安装和配置详解
2019/07/16 Python
Django结合ajax进行页面实时更新的例子
2019/08/12 Python
Python处理session的方法整理
2019/08/29 Python
python实现自动化报表功能(Oracle/plsql/Excel/多线程)
2019/12/02 Python
Python调用钉钉自定义机器人的实现
2020/01/03 Python
html5 利用重力感应实现摇一摇换颜色可用来做抽奖等等
2014/05/07 HTML / CSS
Perry Ellis官网:美国男士品味服装
2016/12/09 全球购物
中国首家奢侈品O2O网购平台:第五大道奢侈品网
2017/12/14 全球购物
如何写出高性能的JSP和Servlet
2013/01/22 面试题
乔迁宴答谢词
2014/01/21 职场文书
挂职自我鉴定
2014/02/26 职场文书
广告词串烧
2014/03/19 职场文书
班级读书活动总结
2014/06/30 职场文书
2014年小学校长工作总结
2014/12/08 职场文书
学校工会工作总结2015
2015/05/19 职场文书
元旦联欢晚会主持词
2015/07/01 职场文书
2016年党课培训学习心得体会
2016/01/07 职场文书