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 相关文章推荐
javascript校验价格合法性实例(必须输入2位小数)
May 05 Javascript
Jquery的each里用return true或false代替break或continue
May 21 Javascript
深入理解JavaScript系列(28):设计模式之工厂模式详解
Mar 03 Javascript
jQuery使用after()方法在元素后面添加多项内容的方法
Mar 26 Javascript
JavaScript中string对象
Jun 12 Javascript
javascript常见数据验证插件大全
Aug 03 Javascript
利用Vue.js指令实现全选功能
Sep 08 Javascript
深究AngularJS中ng-drag、ng-drop的用法
Jun 12 Javascript
vue 运用mock数据的示例代码
Nov 07 Javascript
微信小程序报错: thirdScriptError的错误问题
Jun 19 Javascript
浅谈JavaScript中的“!!”作用
Aug 03 Javascript
JavaScript如何操作css
Oct 24 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 字符串正则替换函数preg_replace使用说明
2011/07/15 PHP
2014过年倒计时示例
2014/01/31 PHP
PHP strip_tags()去除HTML、XML以及PHP的标签介绍
2014/02/18 PHP
PHP中使用imagick生成PSD文件缩略图教程
2015/01/26 PHP
javascript中的一些注意事项 更新中
2010/12/06 Javascript
JavaScript 布尔操作符解析  && || !
2012/08/10 Javascript
JavaScript 模拟类机制及私有变量的方法及思路
2013/07/10 Javascript
jquery.ui.draggable中文文档(原文翻译)
2013/11/15 Javascript
jQuery删除一个元素后淡出效果展示删除过程的方法
2015/03/18 Javascript
JavaScript将数字转换成大写中文的方法
2015/03/23 Javascript
JS通过Cookie判断页面是否为首次打开
2016/02/05 Javascript
基于JS快速实现导航下拉菜单动画效果附源码下载
2016/10/27 Javascript
jquery插件bootstrapValidator数据验证详解
2016/11/09 Javascript
layer插件select选中默认值的方法
2018/08/14 Javascript
vue element动态渲染、移除表单并添加验证的实现
2019/01/16 Javascript
layui radio单选限制下一个radio单选的实例
2019/09/03 Javascript
如何在Node和浏览器控制台中打印彩色文字
2020/01/09 Javascript
vscode 配置vue+vetur+eslint+prettier自动格式化功能
2020/03/23 Javascript
浅谈javascript事件环微任务和宏任务队列原理
2020/09/12 Javascript
对vue生命周期的深入理解
2020/12/03 Vue.js
如何利用JavaScript编写一个格斗小游戏
2021/01/06 Javascript
python使用PyGame绘制图像并保存为图片文件的方法
2015/04/24 Python
python交互模式下输入换行/输入多行命令的方法
2019/07/02 Python
python wxpython 实现界面跳转功能
2019/12/17 Python
英国浴室洗脸盆购物网站:Click Basin
2018/06/08 全球购物
日本高岛屋百货购物网站:TAKASHIMAYA
2019/03/24 全球购物
英国计算机商店:Technextday
2019/12/28 全球购物
《散步》教学反思
2014/03/02 职场文书
学徒工职责
2014/03/06 职场文书
留守儿童工作方案
2014/06/02 职场文书
讲文明知礼仪演讲稿
2014/09/13 职场文书
银行授权委托书样本
2014/10/13 职场文书
护士爱岗敬业心得体会
2016/01/25 职场文书
2019最新婚庆对联集锦!
2019/07/10 职场文书
pytorch中的model.eval()和BN层的使用
2021/05/22 Python
MySQL图形化管理工具Navicat安装步骤
2021/12/04 MySQL