闭包可能是JavaScript里最被人神乎其神的一个概念,世间万物皆凡夫俗子,你觉着他神奇是因为你根本没有了解,所有的事物当你了解透彻后就不会有这种不明觉厉的错觉了。哈哈哈,上来又是一顿哲学普及。

  下面开始进行阐述。

  1.什么是闭包?

  参阅了很多资料,最后比较靠谱的解释就是“闭包是一个函数”。

  闭包是一个函数,什么样的函数才能叫做闭包?可以从他的作用上理解闭包就是能够读取其他函数内部变量的函数。

  2.闭包是怎么形成的?

  首先再回顾下JavaScript的作用域这个概念。JavaScript是函数作用域而不是块状作用域,也就是说JavaScript的作用域是以函数来划分的,函数内部的变量为私有变量,而相对的全局变量是指在所有的函数外部定义的变量。全局变量自动归为全局对象global的属性,在web浏览器中这个global对象由window对象来承担这个全局变量的角色。

  一般的,在函数内部声明的加了var声明符的(函数内部定义变量不加var的话会默认声明为全局变量,为了避免全局空间变量污染所以要谨记)变量,在这个函数执行时候创建该函数相关的作用域,函数当然会有嵌套,所以就会存在作用域链接作用域的问题,也就是所说的作用域链。这个作用域链从最里层函数作用域开始,逐渐向外层链接,一层一层直到最外层的全局作用域。所以在内层的函数使用某个变量的时候会按照这个链接顺序去寻找,如果中途找到则停止,直到全局作用域还没有找到则该变量为未定义。

  正常情况,一个函数在调用开始执行时创建这个函数执行上下文及相应的作用域链,在函数执行结束后释放函数执行上下文及相应作用域链所占的空间。

  明白了作用域后再来看闭包怎样才能读取到其它函数的内部变量这个问题。首先,如果是函数A嵌套函数B,函数B自然可以访问函数A里的变量,向上还可以直到全局作用域的变量。这种情况肯定形成不了闭包。所以闭包是在某函数A之外的函数B想要访问函数A内部的变量下形成的。

  3.闭包怎么样才能读取到其它函数的内部变量

  最常见的形式就是在函数内部返回一个函数,这个返回的函数有引用外部函数的变量。示例代码如下:

function foo() {
var tmp = 0;
return function () {
console.log(tmp = tmp + 1);
}
}
var bar = foo(); // foo()执行后返回内部匿名函数,所以bar现在就是内部的那个函数的引用
bar(); //现在执行bar,也就是执行内部的那个匿名函数,输出为1,
bar(); //紧接着再执行bar,输出为2,也就是说foo函数内部的tmp变量不仅被外部的函数访问到了,而且tmp的值没有被清空,会累加

  这就满足了形成闭包的条件,即相对函数A(这里指foo())而言他外部的函数B(这里指bar(),其实bar只是内部匿名函数的引用在外部执行)访问到了A作用域的变量tmp,而且值的注意的是tmp变量一直存在于内存中。

  依据上边这段代码解释为什么变量会一直存在于内存中而不是随着函数执行完毕后就被垃圾回收。

  首先,bar是在外部作用域中,大部分时候是在全局作用域中,而全局作用域中的变量是一直存在于内存中的,所以当bar引用foo的时候,间接地foo也会一直存在于内存中,既然foo都一直存在于内存中,那foo所形成的函数作用域里的一切都会一直存在于内存中,因此就会出现上边看到的访问到的内部变量不会随着函数执行完后进行垃圾回收的情况。

  4.怎样才能理解闭包?

  首先明白他是一个能访问其它函数内部作用域变量的函数,打破了常规的函数作用域的限制,再理解就是他引用的变量会一直存在于内存中。

  下面贴一些闭包的代码,都是从其他地方看到的:

var object = {
a: 1,
getA: function () {
console.log(this.a=this.a+1);
}
}
object.getA();//输出为2
object.getA();//输出为3

  

var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();

  5.闭包都有什么用处? 

  1)一个是前面提到的可以读取函数内部的变量
  2)另一个就是让这些变量的值始终保持在内存中。
  6.使用闭包需要注意的地方有哪些?
  1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
  2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
 
  完!祝好运。

深入理解JavaScript系列:试着谈谈闭包的更多相关文章

  1. 深入理解JavaScript系列(16):闭包(Closures)

    介绍 本章我们将介绍在JavaScript里大家经常来讨论的话题 —— 闭包(closure).闭包其实大家都已经谈烂了.尽管如此,这里还是要试着从理论角度来讨论下闭包,看看ECMAScript中的闭 ...

  2. 深入理解javascript系列(4):立即调用的函数表达式

    本文来自汤姆大叔 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行. 在详细了解这个之前,我们来谈了解一下“自执行”这个叫法,本文对这个功能的叫法 ...

  3. 深入理解JavaScript系列

    转自http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html 深入理解JavaScript系列(1):编写高质量JavaScript代码 ...

  4. [JS]深入理解JavaScript系列(4):立即调用的函数表达式

    转自:汤姆大叔的博客 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行.在详细了解这个之前,我们来谈了解一下"自执行"这个叫法 ...

  5. 深入理解JavaScript系列(转自汤姆大叔)

    深入理解JavaScript系列文章,包括了原创,翻译,转载,整理等各类型文章,如果对你有用,请推荐支持一把,给大叔写作的动力. 深入理解JavaScript系列(1):编写高质量JavaScript ...

  6. [转]深入理解JavaScript系列

    文章转自:汤姆大叔-深入理解JavaScript系列文章 深入理解JavaScript系列文章,包括了原创,翻译,转载,整理等各类型文章,如果对你有用,请推荐支持一把,给大叔写作的动力. 深入理解Ja ...

  7. [转载]深入理解JavaScript系列 --汤姆大叔

    深入理解JavaScript系列文章,包括了原创,翻译,转载,整理等各类型文章,如果对你有用,请推荐支持一把,给大叔写作的动力. 深入理解JavaScript系列(1):编写高质量JavaScript ...

  8. 深入理解JavaScript系列(转载)

    深入理解JavaScript系列 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 深入理解JavaScript系列(2):揭秘命名函数表达式 深入理解JavaSc ...

  9. 深入理解JavaScript系列(49):Function模式(上篇)

    介绍 本篇主要是介绍Function方面使用的一些技巧(上篇),利用Function特性可以编写出很多非常有意思的代码,本篇主要包括:回调模式.配置对象.返回函数.分布程序.柯里化(Currying) ...

  10. 深入理解JavaScript系列(47):对象创建模式(上篇)

    介绍 本篇主要是介绍创建对象方面的模式,利用各种技巧可以极大地避免了错误或者可以编写出非常精简的代码. 模式1:命名空间(namespace) 命名空间可以减少全局命名所需的数量,避免命名冲突或过度. ...

随机推荐

  1. nodejs新建服务器

    var http = require('http');// var optfile = require('./models/optfile'); http.createServer(function ...

  2. 关于CDN下查找网站真实ip

    关于CDN下查找网站真实ip From  t00ls.net -----雨苁收集 杂乱无章,自己慢慢看,有问题加Q2359795780~~~~~~噗 先来几张t00lslogo             ...

  3. 怎么让我们自己开发的Android程序设为默认启动

    怎么让我们自己开发的Android程序设为默认启动呢?其实很简单,只要在AndroidManifest.xml文件中配置一下首次启动的那个Activity即要. <activity        ...

  4. iOS:iOS中的多控制器管理

    iOS中的控制器有三种创建方式: 1.通过storyboard创建 UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@" ...

  5. Java中sql语句的引号问题

    1..sql语句 在数据库中,当我们查询语句时,会使用类似的语句: Select * from userinfo where userid='1' or 1; Select * from userin ...

  6. Post提交

    以下两种Post提交方法都可行 /// <summary> /// post 数据 /// </summary> /// <param name="url&qu ...

  7. F2.Net工作流引擎系列索引

    索引如下 F2工作流引擎遵循参考WFCM标准规范,符合中国国情特色,更轻量级的工作流引擎,支持多种数据库(mmsqlserver,mysql,oracle),有强大智能的组织模型接口可快速应用到任何基 ...

  8. ListView

    ListView:列表展示数据1.视图 - 在其右上方小箭头点击将视图改为Largelcon:或右键属性在外观View将其改为Details2.设置列头 - 在其右上方小箭头点击选择编辑列,然后添加列 ...

  9. Leetcode 详解(Substing without repeats character)

    Given a string, find the length of the longest substring without repeating characters. Examples: Giv ...

  10. mysql数据库使用

    C#操作Mysql数据库的存储过程,网址 DATEDIFF() 函数返回两个日期之间的天数. 语法 DATEDIFF(date1,date2) date1 和 date2 参数是合法的日期或日期/时间 ...