谈到闭包,人们常常会把匿名函数和闭包混淆在一起。闭包是指由权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数,仍以前面的

createComparisonFunction()函数为例

  

           function createComparisonFunction(propertyName){
return function(object1,object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
};

  在标识的部分,它访问了外部的变量 propertyName 即使这个函数被返回后,仍然可以访问到这个变量(propertyName);这是应为 匿名函数作用域链中包含  createComparisonFunction() 的作用域。

要理解必须掌握作用域的几个概念

  作用域链:

    什么时候会生成?:当代码在一个执行环境中执行时,就会创建变量对象的一个作用域链。

    作用:保证对执行环境有权访问的所以函数和变量的有序访问。作用域的前端永远是当前的执行环境所对应的变量对象;

  执行环境:定义了变量或函数有权访问其他数据,决定它们各自的行为。 每个环境都有一个与之对应的变量对象(variable object)。环境中定义的变量和函数都保存在这个对象中。 当函数执行完成后其执行环境就会被销毁

  变量对象:保存执行环境中的变量和函数。

  活动对象:如果执行环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量。

在函数执行过程中,为读取和写入变量的值,就需要的作用域链中查找对应的变量。先看看一个实例来了解下每个概念对应部分:

  function compare(value1,value2){
 
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
   var result = compare(5,10);
  

   

  先定义了 compare 函数,后又在全局作用域中调用,在这个compare 执行环境为函数的时候,当前活动对象则为变量对象。 活动对象默认是只有 arguments 但是此时却不一样(this,arguments,value1,value2);

而全局作用域中对应变量对象(compare 、result);来看看下图吧!

  <1>在定义compare函数的时候,会创建一个预先包含全局变量对象作用域链,这个作用域链保存在内部的[[Scope]]属性中。

  <2>当在调用 compare 函数的时候,会为函数创建一个执行环境,然后通过赋值函数的[[Scope]]属性中的对象构建起执行环境的作用域链。此后,又有一个活动对象被创建并推入执行环节作用域链的前端。

对于compare函数的执行环境而言,其作用域链中包含两个变量对象(本地活动对象和全局变量对象)。显然,作用域链本质上是一个指向变量对象的指针列表,它引用但不实际包含变量对象。

所以无论什么时候,访问一个变量时都会从作用域链中搜索具有相应名字的变量。一般来讲当函数执行完毕后。局部活动对象就会被销毁,内存中仅保存全局作用域。但是闭包所有不同。

  在闭包中,当函数内部定义一个内部函数,该内部函数会把外部函数的活动对象添加到自己的作用域链中;因此createComparisonFunction()定义的匿名函数,其作用于域会包含外部函数的作用域。

 1           function  createComparisonFunction(propertyName){
2 return function(object1,object2){
3 var value1 = object1[propertyName];
4 var value2 = object2[propertyName];
5 if(value1<value2){
6 return -1;
7 }else if(value1>value2){
8 return 1;
9 }else{
10 return 0;
11 }
12 }
13 };

当下面代码执行时,外部函数与内部函数包含的作用域链, 如下图

 var compare = createComparisonFunction("name");
var result = compare({name:"Nicholas"},{name:"Greg"});

在代码执行过程中,用变量 compare 保存了其返回的内部匿名函数,而作用域链中包含了三个变量对象,(全局变量对象,外部函数-活动对象,匿名函数-活动对象);

所以即使返回后,也还是可以访问到对应的变量; 因为外部函数(createComparisonFunction) 执行完成时,其活动对象依然存在,并不会销毁,因为其被内部函数作用域链中引用了;

虽然 外部函数返回到,其执行环境对应作用域链被销毁,但是其活动对象依然存在。直到匿名函数被销毁,其createComparisonFunction对应的活动对象才会被销毁。

 var compare = createComparisonFunction("name");
var result = compare({name:"Nicholas"},{name:"Greg"});
compare = null; //主要是手动赋值为空,通知系统 进行垃圾处理; 随着其作用域链被销毁(除全局作用域链之外)。

由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内部占用过多。

JavaScript 函数之 ------------------ 闭包的更多相关文章

  1. JavaScript函数、闭包、原型、面向对象

    JavaScript函数.闭包.原型.面向对象 断言 单元测试框架的核心是断言方法,通常叫assert(). 该方法通常接收一个值--需要断言的值,以及一个表示该断言目的的描述. 如果该值执行的结果为 ...

  2. javaScript函数与闭包

    js中函数也是对象,具有一切对象的特征,可以作为表达式给变量赋值,可以作为函数的形参,或者函数的返回值,函数内可以嵌套函数等等,函数内以声明方式定义的函数是局部函数,用表达式声明的函数则由赋值变量的性 ...

  3. JavaScript函数及闭包

    前面一片文章讲到过一点函数,了解到每声明一个函数就会产生一个作用域.而外面的作用域访问不了里面的作用域(把里面的变量和函数隐藏起来),而里面的可以访问到外面的.对于隐藏变量和函数是一个非常有用的技术. ...

  4. javascript函数闭包(closure)

    一,首先感受下javascript函数的闭包 二,闭包 1,定义:闭包就是能够读取其他函数内部变量的函数,由于在javascript语言中,只有在函数内部的子函数才能够读取局部变量,因此可以把闭包简单 ...

  5. JavaScript函数之作用域 / 作用链域 / 预解析

    关于作用域和作用链域的问题,很多文章讲的都很详细,本文属于摘录自己觉得对自己有价值的部分,留由后用,仅供参考,需要查看详细信息请点击我给出的原文链接查看原文件 做一个有爱的搬运工~~ -------- ...

  6. JavaScript权威设计--命名空间,函数,闭包(简要学习笔记十二)

    1.作为命名空间的函数 有时候我们需要声明很多变量.这样的变量会污染全局变量并且可能与别人声明的变量产生冲突. 这时.解决办法是将代码放入一个函数中,然后调用这个函数.这样全局变量就变成了 局部变量. ...

  7. 深入理解javascript函数参数与闭包(一)

    在看此文章,希望先阅读关于函数基础内容 函数定义与函数作用域 的章节,因为这篇文章或多或少会涉及函数基础的内容,而基础内容,我放在函数定义函数作用域 章节. 本文直接赘述函数参数与闭包,若涉及相关知识 ...

  8. 深入理解javascript原型和闭包(2)——函数和对象的关系

    上文(理解javascript原型和作用域系列(1)——一切都是对象)已经提到,函数就是对象的一种,因为通过instanceof函数可以判断. var fn = function () { }; co ...

  9. JavaScript中的闭包和匿名函数

    JavaScript中的匿名函数及函数的闭包   1.匿名函数 2.闭包 3.举例 4.注意 1.匿名函数 函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数:就是没 ...

随机推荐

  1. The working copy xxxx needs to be upgraded to Subversion 1.7.

    原因是我在svn是低版本时候checkout的项目 而对方用的版本比我高 然后 我运行这个项目  就要求我  更新 如果我选择   对项目进行upgrade. 当 upgrade后 我的项目就在 现有 ...

  2. java中字符输入输出流在输出结果的结尾多一个乱码字符:'?'

    原因以及解决方法: 核心方法如下: public static void main(String[] args) throws Exception{ // TODO Auto-generated me ...

  3. Red Hat TimesTen安装记录

    1:内核参数修改 # vi /etc/sysctl.conf kernel.sem= #sysctl –p 备注:此安装过程为测试环境,具体参数修改要参考TimesTen官方文档. 2:创建用户及组信 ...

  4. 复位应答ATR的基本结构和数据元

    根据定义,复位应答是一系列字节的值,这些字节是由卡作为对复位命令的响应发送给接口设备的 ,在I/O电路上,每个字节在一个异步字符中传输.每个成功的复位操作,都会导致I/O上的一个初始字符TS,TS后面 ...

  5. BZOJ1782: [Usaco2010 Feb]slowdown 慢慢游

    1782: [Usaco2010 Feb]slowdown 慢慢游 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 541  Solved: 326[Sub ...

  6. (转)C++ 编程规范

    转载地址:http://www.cnblogs.com/len3d/archive/2008/02/01/1061902.html C/C++编码规范 今天人们越来越明白软件设计更多地是一种工程,而不 ...

  7. 尚学堂 JAVA Day3 概念总结

    java中的运算符 1.算术运算符 + - * / % Arithmetic operators + 运算符有三种身份 Additive Operator 1)加法:如 a + b; 2)连接:如 “ ...

  8. KMP学习

    刚才看了(连接)写的翻译,把kmp算法的工作过程讲的很不错,kmp算法的核心是next,next为什么要那么赋值?其实就是前缀和后缀的最大匹配值,用这个值在匹配失败的时候可以跳过一个不必要的匹配.   ...

  9. F - True Liars - poj1417(背包+并查集)

    题意:有这么一群人,一群好人,和一群坏人,好人永远会说实话,坏人永远说假话,现在给你一组对话和好人与坏人的数目P1, P2. 数据里面的no是A说B是坏人, yes代表A说B是好人,就是这样,问题能不 ...

  10. [置顶] 【Git入门之九】解决冲突

    原创作品,转载请标明:http://blog.csdn.net/jackystudio/article/details/12309531 1.多人协作冲突 如果多人同时修改了同一个文件,那会出现什么样 ...