• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >JavaScript > 理解javascript中的闭包

理解javascript中的闭包

作者:咸鱼老弟 字体:[增加 减小] 来源:互联网 时间:2017-05-11

咸鱼老弟通过本文主要向大家介绍了javascript闭包,javascript闭包函数,javascript中的闭包,什么是javascript闭包,javascript闭包作用等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

阅读目录

  • 什么是闭包?
  • 闭包的特性
  • 闭包的作用:
  • 闭包的代码示例
  • 注意事项
  • 总结

闭包在javascript来说是比较重要的概念,平时工作中也是用的比较多的一项技术。下来对其进行一个小小的总结

什么是闭包?

官方说法:

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量------《javascript高级程序设计第三版》

下面就是一个简单的闭包:

function A(){
 var text="hello world";
 function B(){
 console.log(text);
 }
 return B;
}
var c=A();
c(); // hello world 
</div>

按照字面量的意思是:函数B有权访问函数A作用域中的变量(text),通过另一个函数C来访问这个函数的局部变量text。因此函数B形成了一个闭包。也可以说C是一个闭包,因为C执行的实际是函数B。

这个需要注意的是,直接执行A();是没有任何反应的。因为return B没有执行,除非是return B();

闭包的特性

闭包有三个特性:

 1.函数嵌套函数

 2.函数内部可以引用外部的参数和变量

 3.参数和变量不会被垃圾回收机制回收

解释一下第3点,为什么闭包的参数和变量不会被垃圾回收机制回收呢?

首先我们先了解一下javascript的垃圾回收原理:

(1)、在javascript中,如果一个对象不再被引用,那么这个对象就会被GC(garbage collection)回收;

(2)、如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

上面的示例代码中A是B的父函数,而B被赋给了一个全局变量C(全局变量的生命周期直至浏览器卸载页面才会结束),这导致B始终在内存中,而B的存在依赖于A,因此A也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

闭包的作用:

其实闭包的作用也是有闭包的特性决定的,根据上面的闭包特性,闭包的作用如下:

  1、可以读取函数内部的变量,而不是定义一起全局变量,避免污染环境

  2、让这些变量的值始终保持在内存中。

闭包的代码示例

下面主要介绍几种常见的闭包,并进行解析:

demo1 局部变量的累加。

function countFn(){
 var count=1;
 return function(){  //函数嵌套函数
 count++;
 console.log(count);
 }
}
var y = countFn(); //外部函数赋给变量y;
y(); //2 //y函数调用一次,结果为2,相当于countFn()()
y(); //3 //y函数调用第二次,结果为3,因为上一次调用的count还保存在内存中,没有被销毁,所以实现了累加
y=null; //垃圾回收,释放内存
y(); // y is not a function
</div>

由于第一次执行完,变量count还保存在内存中,所以不会被回收,以致于第二次执行的时候可以对上次的值就行累加。当引入y=null时,销毁引用,释放内存

demo2 循环中使用闭包

代码如下(下面的三个代码示例):我们的目的是想在每次循环中调用循环序号:

demo2-1

for (var i = 0; i < 10; i++) {
 var a = function(){
 console.log(i)
 }
 a() //依次为0--9
}
</div>

这个例子的结果是没有题的,我们依次打印出了0-9

每一层匿名函数和变量i都组成了一个闭包,但是这样在循环中并没有问题,因为函数在循环体中立即被执行了

demo2-2

但是在setTimeout中就不一样了

for(var i = 0; i < 10; i++) {
 setTimeout(function() {
 console.log(i); //10次10
 }, 1000);
}
</div>

我们期望的依次是打印出0--10,实际情况是打印出 10次10。即使吧setTimeout的时间改为0,也是打印出10个10。这是为什么呢?

这是因为setTimeout的一种机制,setTimeout是从任务队列结束的时候开始计时的,如果前面有进程没有结束,那么它就等到它结束再开始计时。在这里,任务队列就是它自己所在的循环。

循环结束setTimeout才开始计时,所以无论如何,setTimeout里面的i都是最后一次循环的 i。该代码中,最后的 i 为10,所以打印出了10个10.

这也就是为什么setTimeout的回调不是每次取循环时的值,而取最后一次的值

demo2-3

解决上面的setTimeout不能依次打印出循环的问题

for(var i=0;i<10;i++){
 var a=function(e){
 return function(){
  console.log(e); //依次输入0--9
 }
 }
 setTimeout(a(i),0);
}
</div>

因为setTimeout第一个参数需要一个函数,所以返回一个函数给它,返回的同时把 i 作为参数传进去,通过形参 e 缓存了i,也就是说e变量相当于是 i 的一个拷贝 ,并带进返回的函数里面。

当 setTimeout 的执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。

也可以用下面的写法,和上面类似:

for(var i = 0; i < 10; i++) {
 (function(e) {
 setTimeout(function() {
  console.log(e); //依次打印出0-9
 }, 0);
 })(i);
}
</div>

demo3 循环中添加事件

看下面的一个典型的demo.

我们希望每次点击li的时候,alert出li的索引值,所以用下面的代码:

<ul id="test">
 <li>第一个</li>
 <li>第二个</li>
 <li>第三个</li>
 <li>第四个</li>
</ul>
var nodes = document.getElementsByTagName("li");
for(i = 0,len=nodes.length;i<len;i++){
 nodes[i].onclick = function(){
 alert(i); //值全是4
 };
}
</div>

事与愿违,无论点击哪一个li,都是alert(4),也就是都是alert循环结束之后的索引值。这是为什么呢?

这是因为循环中为不同的元素绑定事件,事件回调函数里如果调用了跟循环相关的变量,则这个变量取循环的最后一个值。

由于绑定的回调函数是一个匿名函数,所以上面的代码中, 这个匿名函数是一个闭包,携带的作用域为外层作用域(也就是for里面的作用域),当事件触发的时候,作用域中的变量已经随着循环走到最后了。

还有一点就是,事件是需要触发的,而绝大多数情况下,触发的时候循环已经结束了,所以循环相关的变量就是最后一次的取值。

要实现点击li,alert出li的索引值,需要将上面的代码进行以下的修改:

<ul id="test">
 <li>第一个</li>
 <li>第二个</li>
 <li>第三个</li>
 <li>第四个</li>
</ul>
var nodes=document.getElementsByTagName("li");
for(var i=0;i<nodes.length;i++){
 (function(e){
 nodes[i].onclick=function(){
  alert(e);
 };
 })(i)
}
</div>

解决思路: 增加若干个对应的闭包域空间(这里采用的是匿名函数),专门用来存储原先需要引用的内容(下标)。

当立即执行函数执行的时候,e 值不会被销毁,因为它的里面有个匿名函数(也可以说是因为闭包的存在,所以变量不会被销毁)。执行后,e 值 与全局变量 i 的联系就切断了,

也就是说,执行的时候,传进的 i 是多少,立即执行函数的 e 就是多少,但是 e 值不会消失,因为匿名函数的存在。

也可以用下面的解法,原理是一样的:

<ul id="test">
 <li>第一个</li>
 <li>第二个</li>
 <li>第三个</li>
 <li>第四个</li>
</ul>
var nodes=document.getElementsByTagName('li');
for(var i = 0; i<nodes.length;i++){
 (function(){
 var temp = i;
 nodes[i].onclick = function () {
  alert(temp);
 }
 })();
}
</div>

注意事项

1、造成内存泄露

由于闭包会携带包

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 深入理解Javascript中的作用域链和闭包
  • javascript闭包功能与用法实例分析
  • 10道典型的JavaScript面试题
  • 轻松理解JavaScript闭包
  • 轻松学习Javascript闭包
  • 分享一道关于闭包、bind和this的面试题
  • 深入理解Javascript箭头函数中的this
  • javascript笔记之匿名函数和闭包
  • 浅谈javascript的闭包
  • 详谈JavaScript的闭包及应用

相关文章

  • 2017-05-11nodejs入门教程五:连接数据库的方法分析
  • 2017-05-11jquery实现百叶窗效果
  • 2017-05-11JS实现的简单图片切换功能示例【测试可用】
  • 2017-05-11canvas实现简易的圆环进度条效果
  • 2017-05-11bootstrap suggest搜索建议插件使用详解
  • 2017-05-11js时间戳格式化成日期格式的多种方法介绍
  • 2017-05-11详解Angularjs在控制器(controller.js)中使用过滤器($filter)格式化日期/时间实例
  • 2017-05-11JS文件/图片从电脑里面拖拽到浏览器上传文件/图片
  • 2017-05-11canvas绘制多边形
  • 2017-05-11html+javascript+bootstrap实现层级多选框全层全选和多选功能

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • Bootstrap 网格系统布局详解
    • 基于Bootstrap 3 JQuery及RegExp的表单验证功能
    • js JSON介绍
    • JS 实现随机验证码功能
    • BootstrapValidator实现注册校验和登录错误提示效果
    • JavaScript日期选择功能示例
    • 手机端js和html5刮刮卡效果
    • 微信小程序 ES6Promise.all批量上传文件实现代码
    • Bootstrap modal 多弹窗之叠加显示不出弹窗问题的解决方案
    • jQuery插件zTree实现的基本树与节点获取操作示例

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有