源自http://www.cnblogs.com/ljchow/archive/2010/07/06/1768749.html
闭包函数只能访问变量的最终值!!!
eg:
function fnTest(arr) {
for (var i=0;i < arr.length;i++) {
arr[i]=function () { alert(i+" | "+arr[i]); };
}
}
var arr = [0,1,2,3];
fnTest(arr);
for (var i=0;i < arr.length;i++) {
arr[i](); //始终输出4还有一个undefined因为函数退出后,i值为4,所以访问到的值只有4
//结果会连续弹出4个"4|undefined”
}
一. 闭包的理论
首先必须了解以下几个概念:
执行环境
每调用一个函数时(执行函数时),系统会为该函数创建一个封闭的局部的运行环境,即该函数的执行环境。函数总是在自己的执行环境中执行,如读写 局部变量、函数参数、运行内部逻辑。创建执行环境的过程包含了创建函数的作用域,函数也是在自己的作用域下执行的。从另一个角度说,每个函数执行环境都有 一个作用域链,子函数的作用域链包括它的父函数的作用域链。关于作用域、作用域链请看下面。
作用域、作用域链、调用对象
函数作用域分为词法作用域和动态作用域。
词法作用域是函数定义时的作用域,即静态作用域。当一个函数定义时,他的词法作用域就确定了,词法作用域说明的是在函数结构的嵌套关系下,函数 作用的范围。这个时候也就形成了该函数的作用域链。作用域链就是把这些具有嵌套层级关系的作用域串联起来。函数的内部[[scope]]属性指向了该作用 域链。
动态作用域是函数调用执行时的作用域。当一个函数被调用时,首先将函数内部[[scope]]属性指向了函数的作用域链,然后会创建一个调用对 象,并用该调用对象记录函数参数和函数的局部变量,将其置于作用域链顶部。动态作用域就是通过把该调用对象加到作用域链的顶部来创建的,此时的 [[scope]]除了具有定义时的作用域链,还具有了调用时创建的调用对象。换句话说,执行环境下的作用域等于该函数定义时就确定的作用域链加上该函数 刚刚创建的调用对象,从而也形成了新的作用域链。所以说是动态的作用域,并且作用域链也随之发生了变化。再看这里的作用域,其实是一个对象链,这些对象就 是函数调用时创建的调用对象,以及他上面一层层的调用对象直到最上层的全局对象。
譬如全局环境下的函数A内嵌套了一个函数B,则该函数B的作用域链就是:函数B的作用域—>函数A的作用域—>全局window的 作用域。当函数B调用时,寻找某标识符,会按函数B的作用域—>函数A的作用域—>全局window的作用域去寻找,实际上是按函数B的调用 对象—>函数A的调用对象—>全局对象这个顺序去寻找的。也就是说当函数调用时,函数的作用域链实际上是调用对象链。
闭包
在动态执行环境中,数据实时地发生变化,为了保持这些非持久型变量的值,我们用闭包这种载体来存储这些动态数据(看完下面的应用就会很好的体会 这句话)。闭包的定义:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
闭包就是嵌套在函数里面的内部函数,并且该内部函数可以访问外部函数中声明的所有局部变量、参数和其他内部函数。当该内部函数在外部函数外被调用,就生成了闭包。(实际上任何函数都是全局作用域的内部函数,都能访问全局变量,所以都是window的闭包)
譬如下面这个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<script type= "text/javascript" >
function f(x) {
var a = 0;
a++;
x++;
var inner = function () {
return a + x;
}
return inner;
}
var test = f(1);
alert(test());
</script> |
垃圾回收机制:如果某个对象不再被引用,该对象将被回收。
再结合前面所讲的一些概念,在执行var test=f(1)时创建了f的调用对象,这里暂且记作obj,执行完后虽然退出了外部执行环境,但内部函数inner被外部函数f外面的一个变量 test引用。由于外部函数创建的调用对象obj有一个属性指向此内部函数,而现在这个内部函数又被引用,所以调用对象obj会继续存在,不会被垃圾回收 器回收,其函数参数x和局部变量a都会在这个调用对象中得以维持。虽然调用对象不能被直接访问,但是该调用对象已成为内部函数作用域链中的一部分,可以被 内部函数访问并修改,所以执行test()时,可以正确访问x和a。所以说, 当执行了外部函数时,生成了闭包,被引用的外部函数的变量将继续存在。
二. 闭包的应用
应用1:
这个是我在用js模拟排序算法过程遇到的问题。我要输出每一次插入排序后的数组,如果在循环中写成
setTimeout(function() { $("proc").innerHTML += arr + "<br/>"; }, i * 500);
会发现每次输出的都是最终排好序的数组,因为arr数组不会为你保留每次排序的状态值。为了保存会不断发生变化的数组值,我们用外面包裹一层函数来 实现闭包,用闭包存储这个动态数据。下面用了2种方式实现闭包,一种是用参数存储数组的值,一种是用临时变量存储,后者必须要深拷贝。所有要通过闭包存储 非持久型变量,均可以用临时变量或参数两种方式实现。
应用2:
这个是无忧上的例子(点击这里查看原帖),为每个<li>结点绑定click事件弹出循环的索引值。起初写成
id.onclick = function(){alert(i);}
发现最终弹出的都是4,而不是想要的 1、2、3,因为循环完毕后i值变成了4。为了保存i的值,同样我们用闭包实现:
(ps:var a = (function(){})(); 与 var a =new function(){}效果是一样的,均表示自执行函数。)
应用3:
下面的code是缓存的应用,catchNameArr。在匿名函数的调用对象中保存catch的值,返回的对象由于被CachedBox变量 引用导致匿名函数的调用对象不会被回收,从而保持了catch的值。可以通过CachedBox.getCatch("regionId");来操作,若 找不到regionId则从后台取,catchNameArr 主要是为了防止缓存过大。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<script type= "text/javascript" >
var CachedBox = ( function () {
var cache = {}, catchNameArr = [], catchMax = 10000;
return {
getCatch: function (name) {
if (name in cache) {
return cache[name];
}
var value = GetDataFromBackend();
cache[name] = value;
catchNameArr.push(name);
this .clearOldCatch();
return value;
},
clearOldCatch: function () {
if (catchNameArr.length > catchMax) {
delete cache[catchNameArr.shift()];
}
}
};
})();
</script> |
同理,也可以用这种思想实现自增长的ID。
1
2
3
4
5
6
7
8
9
10
|
<script type= "text/javascript" >
var GetId = ( function () {
var id = 0;
return function () {
return id++;
}
})();
var newId1 = GetId();
var newId2 = GetId();
</script> |
应用4:
这个是无忧上月MM的例子(点击这里查看原帖),用闭包实现程序的暂停执行功能,还蛮创意的。
把这个作用延伸下,我想到了用他来实现window.confirm。
看了上面的这些应用,再回到前面的一句话:在动态执行环境中,数据实时地发生变化,为了保持这些非持久型变量的值,我们用闭包这种载体来存储这 些动态数据。这就是闭包的作用。也就说遇到需要存储动态变化的数据或将被回收的数据时,我们可以通过外面再包裹一层函数形成闭包来解决。
当然,闭包会导致很多外部函数的调用对象不能释放,滥用闭包会使得内存泄露,所以在频繁生成闭包的情景下我们要估计下他带来的副作用。
参考http://www.jb51.net/article/24548.htm
http://www.jb51.net/article/24101.htm
相关推荐
闭包是JavaScript编程中至关重要的一个概念,它允许内部函数访问并操作外部函数的局部变量,即使外部函数已经执行完毕。闭包的核心在于保存执行环境,尤其是变量的状态,使得这些状态能够在函数调用之间保持。 1. *...
JavaScript(JS)是一种广泛应用于网页和网络应用的脚本语言,尤其在Web前端开发中扮演着核心角色。在JS的世界里,"闭包"是一个非常关键的概念,它涉及到函数、作用域以及变量持久化等核心知识。这个"Web-前端教程36...
闭包在JavaScript中有很多实际应用,比如模块化、数据封装、异步操作的回调函数等。通过使用闭包,开发者可以创建私有变量,防止全局污染,同时实现函数间的通信,提高代码的复用性和灵活性。 总之,闭包是...
本教程的“JavaScript前端开发程序设计教程(微课版)”旨在为学习者提供一套系统且深入的JavaScript学习资源,帮助他们掌握JavaScript编程基础以及在实际项目中的应用。通过这份教材代码,你将能够深入理解...
本篇文章将深入探讨JavaScript静态资源的压缩技术及其在前端开发中的应用。 1. **JavaScript压缩的必要性** - **提高加载速度**:压缩JavaScript文件可以减少网络传输的数据量,加快文件下载速度。 - **节省带宽*...
此外,随着前端开发复杂性的提升,JavaScript框架如React、Vue.js、Angular等也会被提及,它们能帮助开发者更高效地构建和管理大型Web应用。 五、实验与实践 压缩包中的“清华社-规划教材-实验教材第2版资源及代码...
使用场景及目标:学习并掌握JavaScript的各项基础知识,理解和运用函数和闭包、面向对象编程理念,熟练掌握前端框架的选择和使用技巧,提升网站交互效果和用户体验的同时保障程序性能。 阅读建议:本文内容全面而...
在JavaScript和Vue.js的世界里,前端开发已经成为现代Web应用程序的核心组成部分。这个名为"js+vue前端开发资料"的压缩包包含了一系列关于这两种技术的详细学习材料,旨在帮助开发者全面掌握JavaScript和Vue.js的...
在当前的IT行业中,前端开发领域始终保持着旺盛的生命力,技术更新迭代迅速,对开发者的要求也越来越高。2017年,尽管已是几年前,但那时的前端面试题仍然能反映出当时的主流技术和趋势,对于今天的开发者来说,仍...
在前端开发领域,JavaScript 是一个不可或缺的编程语言,它为网页和应用程序提供了动态交互性。本资源提供的"前端开发实用技术教程代码实例"是针对一本专门讲述前端开发实用技术的书籍,非常适合初学者进行学习和...
适合人群:适合所有前端开发初学者及有一定经验的开发者提升技术水平。 使用场景及目标:适用于希望深入理解和掌握JavaScript核心技术及前沿技术的读者,帮助其更好地开发高性能的Web应用。 其他说明:通过实际案例...
《前端开发html+js+css手册chm》是一款专为前端开发者设计的综合参考资料,包含了HTML5、CSS3和JavaScript的基础到高级知识,旨在提供一个便捷的查询平台,助力开发者在工作中快速解决问题。CHM(Compiled Help ...
通过阅读“前端开发 JavaScript 权威指南 完整版”,开发者不仅可以系统学习JavaScript的基本概念和技术,还能进一步掌握在实际项目中应用这些知识的技巧,提升自己的前端开发能力。这本书可能是你成为JavaScript...
JavaScript是Web开发中不可或缺的一部分,尤其在前端领域更是发挥着至关重要的作用。在这个环境中,有效管理和组织代码变得越来越重要,这就引出了“命名空间”和“闭包”这两个关键概念。本文将深入探讨这两个概念...
前端开发是构建互联网应用程序和网站的重要组成部分,它涉及到用户与网页交互的所有层面。本教程将深入探讨JavaScript这一核心前端技术,为初学者提供全面的知识体系和实践指导。 **JavaScript基础知识** ...
在这个场景中,我们讨论的是一个名为"layer.rar"的压缩包,它包含了一个利用JavaScript闭包技术封装的提示模态框。这个模态框设计得既适应PC设备,也能在不同分辨率的设备上良好运行,体现了响应式设计的概念。 ...
这个"个人收藏的JS前端开发脚本上百例"集合,无疑是开发者们学习和提升技能的宝贵资源。以下是对这些脚本案例中可能涵盖的知识点的详细解释: 1. **基础语法**:JS的基础包括变量、数据类型(如字符串、数字、布尔...
【Web前端开发中级理论考试评分细则_V1.01】主要涵盖了Web前端开发中的核心概念和技术,通过单选题、多选题和判断题的形式进行考核。以下是对这些知识点的详细解析: 一、单选题 1. 单选题在考试中占据了重要的地位...
在前端开发领域,JavaScript是不可或缺的核心技术之一。本教程推荐主要关注JavaScript的深入学习与实践,旨在帮助开发者不断提升技能水平,适应不断变化的前端环境。"JavaScript开发-学习教程"这个标签明确了我们的...