这篇文章其实是要讲闭包的一些初级应用,但是为了将闭包,我们还是从嵌套函数开始说吧,纵使所有的JavaScript函数都可以说是闭包,但是只有当一个嵌套函数被导出到它所定义的作用域之外时,这种闭包才是有趣的。

【嵌套函数】

JavaScript允许嵌入的函数,允许函数用作数据,并且在函数词法作用域下面,可以产生与传统面向对象语言不同的惊人地方。

首先,JavaScript的函数是通过词法来划分作用域的,而不是动态的划分作用域的,于是,函数的是在定义它们的作用域中运行,而不是在执行它们的作用域中运行,所以,当嵌套函数和它的外围函数定义在同一个词法作用域中的时候,是很容易理解的。比如下面很平淡无奇的代码:

varx='global';functionf () {varx='local';functiong() {

alert(x);

}

g();

}

f();//'local'

当f()调用的时候,作用域链可以理解为由两部分组成,包含f这一调用的调用对象,然后后面是全局对象。此时查找x的值,会先从f的调用对象中查找,如果没有,再查找后面全局对象中x。同理,g因为是f的一个嵌套函数,那么,g调用的时候,作用域链应该就是由三部分组成了,g的调用对象,f的调用对象,和全局对象。函数g是要输出x的值,所以会先在g的调用对象中查找x的值,g中没有定义,接下来查找外围f调用对象中x的定义,于是找到了x='local',那么就会输出x,而不会继续往下查找全局对象了。  如果f中也没定义x的值,那么就会继续查找作用域链后面的全局对象,结果就是global了。如果全局对象中也没定义,那么自然就是undefined。

好了,我们对作用域链有了个初步的理解,同时我们知道,闭包有两个比较常用的用途,一个是可以利用它访问到局部变量,另一个是可以把它外围作用域中的变量值存储在内存中而不在函数调用完毕后就销毁。

下面接着看一个平淡无奇的例子,或许可以帮助理解为什么闭包可以把外部变量值保存在内存中了。

functionmakeFunc (x) {returnfunction() {returnx++}

}vara=[makeFunc(0), makeFunc(1), makeFunc(2)];

alert(a[0]());

alert(a[1]());

alert(a[2]());

执行结果为0,1,2 ;也没有什么特别的地方,这也是严格的词法作用域的正常表现。每次makeFunc调用完毕后,它的调用对象会从作用域链中移除,再没有任何对它的引用,最终通过垃圾收集而完结。说的详细一点,我们可以这样理解。

makeFunc每次调用的时候,会为他创建一个调用对象放置到作用域链中。针对makeFunc这个函数而言,这个调用对象包含一个属性x(也就是函数的参数,因为函数参数可以看做调用对象的一个属性),makeFunc会返回一个匿名嵌套函数的引用,接下来这个匿名嵌套函数执行,又会创建一个调用对象,放置到作用域链中,匿名函数返回x的值,(注意:匿名函数的调用对象中是没有x的定义的,于是它会引用到它外围的函数makeFunc的调用对象,访问到x)然后x加1,至此,匿名函数执行完毕,它调用对象从作用域链中移除, 然后makeFunc也执行完毕,makeFunc调用对象也被移除。由于它的调用对象中包含x,所以x也随着它的销毁而销毁。不会保存下来。

以上就是函数的详细的执行过程,请仔细理解后看看下面改动的代码:

varx=0;functionmakeFunc () {returnfunction() {returnx++}

}vara=[makeFunc(), makeFunc(), makeFunc()];

alert(a[0]());

alert(a[1]());

alert(a[2]());

现在x是一个全局变量了,执行结果为0,1,2;但是这个结果就与上面的有些不同了。下面我们还是从作用域链的方向来理解这个结果产生的原因。

同样,makeFunc每次调用的时候会创建一个调用对象到作用域链中,由于它返回内部嵌套函数的引用,所以内部嵌套函数开始执行,又创建一个嵌套函数的调用对象到作用域链。然后返回x的值,注意,这里就不同了,嵌套函数的调用对象中没有x,它外围的makeFunc的调用对象中也没有x,只能接着往下查找到全局对象中,在全局对象中找到了x的定义,于是正常执行,返回x的值,x加1,然后嵌套函数完毕,调用对象移除,接着makeFunc完毕,调用对象也移除,可是因为他们的调用对象中都没有x,他们的调用对象销毁根本不会影响到x。于是,全局变量x值的改变就这样被保存下来了。

注意,上面说的访问外围的调用对象只是为了帮助理解而不严格的说法,JavaScript不会以任何方式直接访问调用对象,但是,它定义的属性作为调用对象中作用域链的一部分,还是“活的”。另外,如果一个外围函数包含了两个或多个嵌套函数都对全局对象有引用,那么这些嵌套函数都共享同一个全局调用对象,并且其中一个对全局对象的改变对其他的都是可见的。

好了,在JavaScript里,函数是将要执行的代码以及执行这些代码的作用域构成的一个综合体,广义的说,我们就可以把这种代码和作用域的综合体叫做闭包。

【闭包】

我们偶尔需要写一个需要通过调用来记住一个变量值的函数。于是,如果我们了解了作用域,就会知道,局部变量是很难做到的,因为函数的调用对象不能在调用后一直维持。全局变量可以做到,就如上面的例子一样,但是这样很容易造成全局变量污染。既然调用对象不能维持,那么我们不把值保存在调用对象中不就行了?!所以,下面是实现的一种方法:用函数对象自身的属性来保存。

uniqueID=function() {if(!arguments.callee.id) arguments.callee.id=0;returnarguments.callee.id++;

}

alert(uniqueID());//0alert(uniqueID());//1

如上,因为函数本身就是一个对象,所以,我们用它自身的一个属性来保存是可行的,但是这样做有一个问题,就是任何人在任何时候都可以通过unqueID.id强制访问到我们原本保存到的值并作出修改。这是我们不愿看到的。

所以,通常,我们使用闭包来实现这件事。如下:

_uniqueID=(function(){varid=0;returnfunction() {returnid++}

})();

alert(_uniqueID());//0alert(_uniqueID());//1

同样,我们也用作用链域来解释下结果。注意到_uniqueID本身就是一个匿名函数,它内部又有个匿名嵌套函数,我们直接调用的是_uniqueID(),也就是说,我们直接调用的其实是_uniqueID内部的嵌套函数,而它本身的调用对象没有定义id,于是引用外围的调用对象中的id,并返回,id加1,执行完毕,内层嵌套函数调用对象移出作用域链。而外围的id并没有被销毁,于是就这样保存了下来。

有人可能会疑惑,不是说调用对象在函数执行完毕后就移除了作用域链吗,外围匿名函数(function(){})();也是调用完毕了的,应该调用对象也没了才对。

是的,调用对象是在当前函数执行完毕后就结束引用,但是这里不要误解了上面_uniqueID()的调用,他并不是直接调用的外围函数,而是调用的嵌套函数,嵌套函数的作用域链是包含外围函数的作用域链的。所以在它的调用对象移除作用域链的时候是能够访问到这条作用域链上其他对象的属性并改变的。

闭包本身就是个难以理解但是又非常有用的东西,希望能对有需要的人一些帮助吧。此外,资历所限,本人理解也可能有误,如发现,敬请指正。

来源: http://www.cnblogs.com/hongru/archive/2010/10/30/1865041.html

tr闭包_嵌套函数及闭包相关推荐

  1. Object-C---Swift之(七)嵌套函数与闭包

    嵌套函数 函数分为全局函数和局部函数,前面已经介绍了全局函数,下面介绍下局部函数,定义在函数体内部的函数可以成为嵌套函数. 注意:嵌套函数对外是隐蔽的,只能在其封闭函数内有效,嵌套函数只能在其封闭函数 ...

  2. python函数嵌套闭包_闭包与嵌套函数

    没有区别,因为没有闭合函数,不是真的.有closures,但这一术语指的是在外部和内部范围中保存变量的技术.只剩下嵌套函数.在 嵌套函数只是在另一个函数内部定义的函数:def outer(): def ...

  3. python高阶函数闭包装饰器_Python自学从入门到就业之高阶函数、嵌套函数、闭包、装饰器...

    高阶函数 在Python中,函数其实也是一种数据类型. def test(): return 'hello world' print(type(test)) # 函数对应的数据类型是 function ...

  4. Python基础教程:嵌套函数、闭包

    嵌套函数 在函数中定义另一个函数称为嵌套函数. 嵌套函数可以访问包围范围内的变量. def print_msg(msg):def printer():print(msg)printer()print_ ...

  5. 浅谈Javascript -- 【嵌套函数及闭包】

    这篇文章其实是要讲闭包的一些初级应用,但是为了将闭包,我们还是从嵌套函数开始说吧,纵使所有的JavaScript函数都可以说是闭包,但是只有当一个嵌套函数被导出到它所定义的作用域之外时,这种闭包才是有 ...

  6. python闭包详解函数_Python闭包详解

    首先给出闭包函数的必要条件: 闭包函数必须返回一个函数对象 闭包函数返回的那个函数必须引用外部变量(一般不能是全局变量),而返回的那个函数内部不一定要return 几个典型的闭包例子: #ENV> ...

  7. python 嵌套函数过多_嵌套函数的性能开销是多少?

    性能惩罚肯定存在.如果函数是在对另一个函数的调用中创建的,那么每次调用外部函数时都会创建函数对象.但这种惩罚很小,而且通常可以忽略不计.尤其要考虑到一个显而易见的事实:在大多数情况下,只有当嵌套函数不 ...

  8. 一文搞懂:词法作用域、动态作用域、回调函数、闭包

    把以前一直只限于知道,却不清晰理解的这几个概念完完整整地梳理了一番.内容参考自wiki页面,然后加上自己一些理解. 词法作用域和动态作用域 不管什么语言,我们总要学习作用域(或生命周期)的概念,比如常 ...

  9. php 递归函数自调用函数,PHP闭包实现函数的自调用实现递归

    PHP的闭包可能不常用,但是在某些场合之下还是可以考虑用php的闭包来实现某些功能的,比如递归,这里讲一下用php的闭包实现递归 //php闭包实现函数的自调用,也就是实现递归 function cl ...

最新文章

  1. USEARCH11新功能——OTU表抽平otutab_rare
  2. CUDA学习-计算实际线程ID
  3. ASP.NET Core 反向代理部署知多少
  4. 通信工程施工设计纲领文件
  5. 使用yum命令安装mysql_Linux Centos 下使用yum 命令安装mysql实现步骤
  6. 在文本框中插入@对象
  7. 鸿蒙系统官网电脑版,华为鸿蒙系统官方pc版下载-华为鸿蒙系统官方电脑pc版 -优盘手机站...
  8. 和利时dcs系统服务器设置,和利时DCS系统组态流程
  9. 数值分析思考题(钟尔杰版)参考解答——第一章
  10. 用JSON-server模拟REST API(二) 动态数据
  11. 【数据应用案例】金融行业大数据用户画像实践
  12. 多人网络(Valve开发文档翻译[起源引擎])(一)
  13. 鸿蒙系统能把游戏窗口化吗,家里装上华为智慧屏S系列,这三个功能,让我爱不释手!...
  14. 如何在scrapy中捕获并处理各种异常
  15. Attention机制介绍(原理+代码)
  16. 蚁群算法(ant system,AS)
  17. 用canvas画一个太极图(八卦图)
  18. CSS 强制换行和禁止换行
  19. MySQL数据类型 -- 日期时间型
  20. 服务器pe安装win7系统安装教程,U盘PE安装Win7安装版|U盘安装win7原版教程

热门文章

  1. ajax请求拿到多条数据拼接显示在页面中
  2. 正确判断js数据类型 总结记录
  3. 谈谈一些有趣的CSS题目(十五)-- 谈谈 CSS 关键字 initial、inherit 和 unset
  4. 微信群运营之设计运营思路
  5. 判断字符串是否为正整数 浮点小数
  6. 【谈谈IO】BIO、NIO和AIO
  7. C语言操作mysql
  8. Redis-主从复制
  9. python高阶函数介绍_python高级特性和高阶函数及使用详解
  10. php根据id查找条件怎么写_thinkphp where()条件查询