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 相关文章推荐
用window.location.href实现刷新另个框架页面
Mar 07 Javascript
用javascript getComputedStyle获取和设置style的原理
Oct 10 Javascript
jquery获取input表单值的代码
Apr 19 Javascript
来自qq的javascript面试题
Jul 24 Javascript
jquery插件制作 提示框插件实现代码
Aug 17 Javascript
JS实现淘宝幻灯片效果的实现方法
Mar 22 Javascript
基于jquery实现控制经纬度显示地图与卫星
May 20 Javascript
js点击文本框弹出可选择的checkbox复选框
Feb 03 Javascript
jquery注册文本框获取焦点清空,失去焦点赋值的简单实例
Sep 08 Javascript
移动端web滚动分页的实现方法
May 05 Javascript
帝国cms首页列表页实现点赞功能
Oct 30 Javascript
js中document.write和document.writeln的区别
Mar 11 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
Yii框架弹出窗口组件CJuiDialog用法分析
2017/01/07 PHP
php 提交表单 关闭layer弹窗iframe的实例讲解
2018/08/20 PHP
PHP count_chars()函数讲解
2019/02/14 PHP
FF火狐下获取一个元素同类型的相邻元素实现代码
2012/12/15 Javascript
js限制checkbox选中个数以限制六个为例
2014/07/15 Javascript
js 动态修改css文件用到了cssRule
2014/08/20 Javascript
jQuery获取标签文本内容和html内容的方法
2015/03/27 Javascript
JS中对象与字符串的互相转换详解
2016/05/20 Javascript
Javascript基础学习笔记(菜鸟必看篇)
2016/07/22 Javascript
老生常谈jquery中detach()和remove()的区别
2017/03/02 Javascript
js中DOM三级列表(代码分享)
2017/03/20 Javascript
微信小程序 图片宽高自适应详解
2017/05/11 Javascript
利用10行js代码实现上下滚动公告效果
2017/12/08 Javascript
iview中Select 选择器多选校验方法
2018/03/15 Javascript
Python的Django中将文件上传至七牛云存储的代码分享
2016/06/03 Python
利用python爬取软考试题之ip自动代理
2017/03/28 Python
Python干货:分享Python绘制六种可视化图表
2018/08/27 Python
Python使用matplotlib绘制随机漫步图
2018/08/27 Python
利用python实现对web服务器的目录探测的方法
2019/02/26 Python
Python使用itchat模块实现简单的微信控制电脑功能示例
2019/08/26 Python
python return逻辑判断表达式实现解析
2019/12/02 Python
python判断变量是否为int、字符串、列表、元组、字典的方法详解
2020/02/13 Python
Python 没有main函数的原因
2020/07/10 Python
Node.js 和 Python之间该选择哪个?
2020/08/05 Python
利用python3筛选excel中特定的行(行值满足某个条件/行值属于某个集合)
2020/09/04 Python
体验完美剃须:The Art of Shaving
2018/08/06 全球购物
剪枝的学问教学反思
2014/02/07 职场文书
消防安全承诺书
2014/05/22 职场文书
社区健康教育工作方案
2014/06/03 职场文书
2014年禁毒工作总结
2014/11/24 职场文书
采购员岗位职责范本
2015/04/07 职场文书
校车司机安全责任书
2015/05/11 职场文书
城镇居民医疗保险工作总结
2015/08/10 职场文书
社区志愿者服务心得体会
2016/01/22 职场文书
python入门之算法学习
2021/04/22 Python
在Python中如何使用yield
2021/06/07 Python