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日历 推荐
Dec 03 Javascript
不用写JS也能使用EXTJS视频演示
Dec 29 Javascript
event.keyCode键码值表 附只能输入特定的字符串代码
May 15 Javascript
jQuery的实现原理的模拟代码 -1 核心部分
Aug 01 Javascript
JavaScript中的console.profile()函数详细介绍
Dec 29 Javascript
JS获取时间的方法
Jan 21 Javascript
深入解析JavaScript的闭包机制
Oct 20 Javascript
基于javascript实现根据身份证号码识别性别和年龄
Jan 22 Javascript
vue工程全局设置ajax的等待动效的方法
Feb 22 Javascript
微信公众平台获取access_token的方法步骤
Mar 29 Javascript
解决node终端下运行js文件不支持ES6语法
Apr 04 Javascript
React 高阶组件HOC用法归纳
Jun 13 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语法速查表
2006/12/06 PHP
php实现网页缓存的工具类分享
2015/07/14 PHP
php实现购物车产品删除功能(2)
2020/07/23 PHP
一段利用WSH获取登录时间的jscript代码
2008/05/11 Javascript
javascript代码运行不出来执行错误的可能情况整理
2013/10/18 Javascript
extjs4 treepanel动态改变行高度示例
2013/12/17 Javascript
JavaScript中的操作符==与===介绍
2014/12/31 Javascript
node.js下LDAP查询实例分享
2015/09/30 Javascript
Knockout自定义绑定创建方法
2015/12/26 Javascript
理解javascript中Map代替循环
2016/02/26 Javascript
Three.js学习之几何形状
2016/08/01 Javascript
bootstrap confirmation按钮提示组件使用详解
2017/08/22 Javascript
详解几十行代码实现一个vue的状态管理
2019/01/28 Javascript
mpvue全局引入sass文件的方法步骤
2019/03/06 Javascript
深入理解javascript prototype的相关知识
2019/09/19 Javascript
jQuery实现聊天对话框
2020/02/08 jQuery
解决element-ui里的下拉多选框 el-select 时,默认值不可删除问题
2020/08/14 Javascript
更改Python命令行交互提示符的方法
2015/01/14 Python
深入讲解Python中面向对象编程的相关知识
2015/05/25 Python
Python中数字以及算数运算符的相关使用
2015/10/12 Python
tensorflow 获取模型所有参数总和数量的方法
2018/06/14 Python
使用 Python 实现文件递归遍历的三种方式
2018/07/18 Python
提升Python效率之使用循环机制代替递归函数
2019/07/23 Python
tensorflow多维张量计算实例
2020/02/11 Python
Python Socketserver实现FTP文件上传下载代码实例
2020/03/27 Python
快速了解Python开发环境Spyder
2020/06/29 Python
Selenium python时间控件输入问题解决方案
2020/07/22 Python
迪梵英国官方网站:Darphin英国
2017/12/06 全球购物
Silk Therapeutics官网:清洁、抗衰老护肤品
2020/08/12 全球购物
信息工程学院毕业生推荐信
2013/11/05 职场文书
机械系毕业生求职信
2014/05/28 职场文书
个人债务授权委托书
2014/10/17 职场文书
终止解除劳动合同证明书
2015/06/17 职场文书
你离财务总监还有多远?速览CFO的岗位职责
2019/11/18 职场文书
Mac环境Nginx配置和访问本地静态资源的实现
2021/03/31 Servers
【海涛教你打dota】体验一超神发条:咱是抢盾专业户
2022/04/01 DOTA