当前位置: 首页 > news >正文

浅谈对JS闭包的理解

函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性称为“闭包”。换言之,函数定义时的作用域链到函数执行时总是有效的。通俗一点,闭包就是能够读取其他函数内部变量的函数。它的用途:一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

什么事作用域链呢?作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。当JavaScript需要查找变量X的值的时候,他会从链中的第一个对象开始查找,如果这个对象有一个名为X的属性,则直接会使用这个属性的值,如果第一个对象中不存在名为X的属性,JavaScript会继续查找链上的下一个对象,以此类推。当定义一个函数时,他实际上保存一个作用域链。当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存到那个作用域链上,同时创建一个新的更长的表示函数调用作用域的链。

接着再说一说JS的内存机制。一般来说,一个函数在执行开始的时候,会给其中定义的变量划分内存空间保存,以备后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了.对应的内存空间也就被回收了.下次再执行此函数的时候,所有的变量又回到最初的状态,重新赋值使用.但是如果这个函数内部又嵌套了另一个函数,而这个函数是有可能在外部被调用到的.并且这个内部函数又使用了外部函数的某些变量的话.这种内存回收机制就会出现问题.如果在外部函数返回后,又直接调用了内部函数,那么内部函数就无法读取到他所需要的外部函数中变量的值了.所以js解释器在遇到函数定义的时候,会自动把函数和他可能使用的变量(包括本地变量和父级和祖先级函数的变量(自由变量))一起保存起来.也就是构建一个闭包,这些变量将不会被内存回收器所回收,只有当内部的函数不可能被调用以后(例如被删除了,或者没有了指针),才会销毁这个闭包,而没有任何一个闭包引用的变量才会被下一次内存回收启动时所回收.

看看下面一段代码.

复制代码
var result=[];
function foo(){
    var i= 0;
    for (;i<3;i=i+1){
        result[i]=function(){
            alert(i)
        }
    }
};
foo();
result[0](); // 3
result[1](); // 3
result[2](); // 3
复制代码

这段代码中,程序员希望foo函数中的变量i被内部循环的函数使用,并且能分别获得他们的索引,而实际上,只能获得该变量最后保留的值,也就是说.闭包中所记录的自由变量,只是对这个变量的一个引用,而非变量的值,当这个变量被改变了,闭包里获取到的变量值,也会被改变.

解决的方法之一,是让内部函数在循环创建的时候立即执行,并且捕捉当前的索引值,然后记录在自己的一个本地变量里.然后利用返回函数的方法,重写内部函数,让下一次调用的时候,返回本地变量的值,改进后的代码:

复制代码
var result=[];
function foo(){
    var i= 0;
    for (;i<3;i=i+1){
        result[i]=(function(j){
            return function(){
                alert(j);
            };
        })(i);
    }
};
foo();
result[0](); // 0
result[1](); // 1
result[2](); // 2
复制代码


参考网址:http://www.cnblogs.com/mzwr1982/archive/2012/05/20/2509295.html

http://www.jb51.net/article/24101.htm


相关文章:

  • 分享几款js矢量图类库
  • Swiper说明及API手册说明
  • 浏览器兼容性汇总
  • 关于Ajax的一些问题
  • js内存泄露问题
  • javascript小数乘法精确率问题
  • JS 和 Java Date时间格式的转换
  • WebApp 的 meta 标签
  • css3pie 解决IE下CSS3的兼容性问题
  • CSS3的REM设置字体大小
  • 网页中导入特殊字体@font-face属性详解
  • js深拷贝和浅拷贝
  • jQuery.fn的作用是什么
  • js正则表达test、exec和match的区别
  • JavaScript 模块化编程 - Module Pattern
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • javascript从右向左截取指定位数字符的3种方法
  • maya建模与骨骼动画快速实现人工鱼
  • NSTimer学习笔记
  • Python 反序列化安全问题(二)
  • Rancher-k8s加速安装文档
  • React+TypeScript入门
  • ReactNative开发常用的三方模块
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 中文输入法与React文本输入框的问题与解决方案
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • (Oracle)SQL优化技巧(一):分页查询
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (转)C语言家族扩展收藏 (转)C语言家族扩展
  • .NET 6 在已知拓扑路径的情况下使用 Dijkstra,A*算法搜索最短路径
  • .Net Core 中间件验签
  • .Net 代码性能 - (1)
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET值类型变量“活”在哪?
  • /etc/shadow字段详解
  • @SpringBootApplication 包含的三个注解及其含义
  • [ 云计算 | AWS 实践 ] 基于 Amazon S3 协议搭建个人云存储服务
  • [].shift.call( arguments ) 和 [].slice.call( arguments )
  • [20190401]关于semtimedop函数调用.txt
  • [ai笔记9] openAI Sora技术文档引用文献汇总
  • [ASP]青辰网络考试管理系统NES X3.5
  • [BZOJ1060][ZJOI2007]时态同步 树形dp
  • [C++核心编程](四):类和对象——封装
  • [Deepin 15] 编译安装 MySQL-5.6.35
  • [Firefly-Linux] RK3568 pca9555芯片驱动详解
  • [iOS开发]iOS中TabBar中间按钮凸起的实现
  • [Mac软件]Boxy SVG 4.20.0 矢量图形编辑器