javascript系列2 -- 闭包详解
转发请标明来源:http://www.cnblogs.com/johnhou/p/javascript.html 请尊重笔者的劳动成果 --John Hou
今天我们从内存结构上来讲解下 javascript中的闭包概念。
闭包:是指有权访问另外一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另外一个函数。
在javascript中没有块级作用域,一般为了给某个函数申明一些只有该函数才能使用的局部变量时,我们就会用到闭包,这样我们可以很大程度上减少全局作用域中的变量,净化全局作用域。
使用闭包有如上的好处,当然这样的好处是需要付出代价的,代价就是内存的占用。
如何理解上面的那句话呢?
每个函数的执行,都会创建一个与该函数相关的函数执行环境,或者说是函数执行上下文。这个执行上下文中有一个属性 scope chain(作用域链指针),这个指针指向一个作用域链结构,作用域链中的指针又都指向各个作用域对应的活动对象。正常情况,一个函数在调用开始执行时创建这个函数执行上下文及相应的作用域链,在函数执行结束后释放函数执行上下文及相应作用域链所占的空间。
比如:
//声明函数
function test(){
var str = "hello world";
console.log(str);
}
//调用函数
test();
在调用函数的时候会在内存中生成如下图的结构:

但是闭包的情况就有点特殊了,由于闭包函数可以访问外层函数中的变量,所以外层函数在执行结束后,其作用域活动对象并不会被释放(注意,外层函数执行结束后执行环境和对应的作用域链就会被销毁),而是被闭包函数的作用域链所引用,直到闭包函数被销毁后,外层函数的作用域活动对象才会被销毁。这也正是闭包要占用内存的原因。
所以使用闭包有好处,也有坏处,滥用闭包会造成内存的大量消耗。
使用闭包还有其他的副作用,可以说是bug,也可以说不是,相对不同的业务可能就会有不同的看法。
这个副作用是 闭包函数只能取到外层函数变量的最终值。
测试代码如下:(这里使用了jquery对象)
/*闭包缺陷*/
(function($){
var result = new Array(),
i = 0;
for(;i<10;i++){
result[i] = function(){
return i;
};
}
$.RES1 = result;
})(jQuery);
// 执行数组中的函数
$.RES1[0]();
上面的代码先通过匿名函数表达式开辟了一块私有作用域,这个匿名函数就是我们上面所说的外层函数,该外层函数有一个参数$,同时还定义了变量result 和 I , 通过for循环给数组result赋值一个匿名函数,这个匿名函数就是闭包,他访问了外层函数的变量I , 理论上数组result[i]() 会返回相应的数组下标值,实际情况却不如所愿。
如上代码 $.RES1[0]() 的执行结果是10.
为什么会这样呢,因为i的最终值就是10.
下面我们通过下图来详细说明下,上面的那段代码执行时在内存中到底发生了什么:

那么这个副作用有没有办法可以修复呢?当然可以!
我们可以通过下面的代码来达到我们的预期。
/*修复闭包缺陷*/
(function($){
var result = new Array(),
i = 0;
for(;i<10;i++){
result[i] = function(num){
return function(){
return num;
}
}(i);
}
$.RES2 = result;
})(jQuery);
//调用闭包函数
console.log($.RES2[0]());
上面的代码又在内存中发生了什么?我们同样用下面的一幅图来详细解释。看懂了上面的图,我们也就不难理解下面的图。

以下是我的微信公众号,技术大牛集结号,欢迎您的关注!

javascript系列2 -- 闭包详解的更多相关文章
- javaScript系列:JSON详解
JSON详解 JSON的全称是”JavaScript Object Notation”,意思是JavaScript对象表示法,它是一种基于文本,独立于语言的轻量级数据交换格式.XML也是一种数据交 ...
- JavaScript中的闭包详解
闭包是JavaScript的重要特性,非常强大,可用于执行复杂的计算,可并不容易理解,尤其是对之前从事面向对象编程的人来说,对 JavaScript 认识和编程显得更难.特别是在看一些开源的JavaS ...
- JavaScript系列文章:详解正则表达式之一
正则表达式是一个精巧的利器,经常用来在字符串中查找和替换,JavaScript语言参照Perl,也提供了正则表达式相关模块,开发当中非常实用,在一些类库或是框架中,比如jQuery,就存在大量的正则表 ...
- JavaScript系列文章:详解正则表达式之三
在上两篇文章中博主介绍了JavaScript中的正则常用方法和正则修饰符,今天准备聊一聊元字符和高级匹配的相关内容. 首先说说元字符,想必大家也都比较熟悉了,JS中的元字符有以下几种: / \ | . ...
- JavaScript系列----AJAX机制详解以及跨域通信
1.Ajax 1.1.Ajax简介 Ajax简介这一部分我们主要是谈一下ajax的起源,ajax是什么?因为这些是跟技术无关的.所以,大多细节都是一笔带过. Ajax的起源? Ajax一词源于2005 ...
- JavaScript系列文章:详解正则表达式之二
在上一篇文章中我们讲了正则表达式的基本用法,接下来博主想聊聊其中的细节,今天就从正则修饰符开始吧. 正则修饰符又称为正则标记(flags),它会对正则的匹配规则做限定,进而影响匹配的最终结果.在上次的 ...
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
- (转)javascript中event对象详解
原文:http://jiajiale.iteye.com/blog/195906 javascript中event对象详解 博客分类: javaScript JavaScriptCS ...
- Javascript 异步加载详解
Javascript 异步加载详解 本文总结一下浏览器在 javascript 的加载方式. 关键词:异步加载(async loading),延迟加载(lazy loading),延迟执行(lazy ...
随机推荐
- Hbase 备份的方式
HBase 备份的方式有三种: 1.下线备份 (1)停止集群. (2)Distcp (3)restore 2.在线备份 -replication 3.在线北大 -CopyTable 4.在线备份-Ex ...
- Android Xlistview的源码浅度分析 监听ListView上下滑动 以及是否到顶和底部
如转载 请注明出处 http://blog.csdn.net/sk719887916 比如我们很多项目中会用到listview 并且要对listview滑动方向进行判断 也有需要的到listview是 ...
- web报表工具FineReport最经常用到部分函数详解
之前分别列出来了finereport常用的文本.时间函数的解释,这里应广大朋友的要求,整理了finereport最常用到的一些函数! SUM SUM(number1,number2,-):求一个指定单 ...
- SharePoint WebPart 简单的读取列表内容的web部件
最近,自己也在学习写一些SharePoint的部件,也就是使用对象模型,下面,介绍一下自己刚刚写的小测试程序,不足之处,还请指正. 1. 新建项目 Vs2008 – 新建 – 项目 – 类库 – 输 ...
- Digogo ugdx文件的制作
The openplatform source code is in old IT FTP server at "vte/KCD/20150814/openplatform_wallace. ...
- 恶补web之五:dhtml学习
dhtml是一种使html页面具有动态特性的艺术.对于多数人来说dhtml意味着html(html DOM),样式表和javascript的组合. dhtml不是w3c标准.dhtml指动态html, ...
- Redis的集群配置
如果我们redis的压力很大,如果我们的并发高到我们读数据和写数据都有了很大压力. 那么我们可能就需要把redis分开部署,并且配置为一个『主从』的状态. 在服务器上构筑Redis的集群配置: 1.切 ...
- VS2010断点调试技巧
设置断点:在如下图中的红色圆点处设置断点,红色圆点表示已经在这行设置断点.快捷键F9. 启动调试:按F5或者点击左边红框中的按钮.右边框是开始执行(不调试)Ctrl+F5. 调试工具栏:下面是工具 ...
- css 字体两端对齐
我想作为一个前端工作者,总会遇到这样的场景,一个表单展示的字段标题有4个字也有2个字的时候,这样子同时存在想展示的美观一点,就需要字体两端对齐了,其实实现方式很简单,我针对其中一种来做下介绍,以后方法 ...
- php coding中的一些小问题
最近在SAE上写微博应用,碰到一些小问题,记下来,以供参考: 1.出错提示: Fatal error: Can't use function return value in write context ...