javascript闭包(Closure)用法实例简析


Posted in Javascript onNovember 30, 2015

本文实例讲述了javascript闭包(Closure)用法。分享给大家供大家参考,具体如下:

closure被翻译成“闭包”,感觉这东西被包装的太学术化。下面参考书本和网上资源简单探讨一下(理解不当之处务请留意)。

1、什么是闭包

官方的回答:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

看了上面的定义,如果你不是高手,我坚信你会和我一样愤怒的质问:这tmd是人话吗?
要理解闭包,还是代码最有说服力啊,上代码:

function funcTest()
{
 var tmpNum=100; //私有变量
 //在函数funcTest内定义另外的函数作为funcTest的方法函数
 function innerFuncTest(
 {
    alert(tmpNum); //引用外层函数funcTest的临时变量tmpNum
 }
 return innerFuncTest; //返回内部函数
}
//调用函数
var myFuncTest=funcTest(); 
myFuncTest();//弹出100

上面代码中,注释已经写的清清楚楚。现在我们可以这么理解“闭包”:在函数体内定义另外的函数作为目标对象的方法函数(示例中就是在函数funcTest内定义另外的函数innerFuncTest作为funcTest的方法函数),而这个对象的方法函数反过来引用外层函数体中的临时变量(闭包是一种间接保持变量值的机制。示例中就是内部函数innerFuncTest引用外层函数funcTest的临时变量tmpNum,这里必须注意,临时变量可以包括外部函数中声明的所有局部变量、参数和声明的其他内部函数)。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包(示例中,调用函数的时候,myFuncTest实际调用的是innerFuncTest函数,也就是说funcTest的一个内部函数innerFuncTest在funcTest之外被调用,这时就创建了一个闭包)。

2、两个利用闭包的例子

下面举两个例子,一个是因为闭包导致了问题,而另一个则利用闭包巧妙地通过函数的作用域绑定参数。

这两个例子相关的HTML标记片断如下:

<a href="#" id="closureTest0">利用闭包的例子(1秒后会看到提示)</a><br />
<a href="#" id="closureTest1">由于闭包导致问题的例子1</a><br />
<a href="#" id="closureTest2">由于闭包导致问题的例子2</a><br />
<a href="#" id="closureTest3">由于闭包导致问题的例子3</a><br />

(1)、因闭包而导致问题

上面的HTML标记片断中有4个<a>元素,现在要给后三个指定事件处理程序,使它们在用户单击时报告自己在页面中的顺序,比如:当用户单击第2个链接时,报告“您单击的是第1个链接”。为此,如果编写下列为后三个链接添加事件处理程序的函数:

function badClosureExample(){
  for (var i = 1; i <4; i++) {
    var element = document.getElementById('closureTest' + i);
    element .onclick = function(){
      alert('您单击的是第' + i + '个链接');
    }
  }
}

然后,在页面载入完成后(不然可能会报错)调用该函数:

window.onload = function(){
  badClosureExample();
}

看一下运行结果,此时单击后3个链接,会看到警告框中显示什么信息呢?——全都是“您单击的是第4个链接”。是不是令你感到十分意外?为什么?

分析:因为在badClosureExample()函数中指定给element.onclick的事件处理程序,也就是onclick那个匿名函数是在badClosureExample()函数运行完成后(用户单击链接时)才被调用的。而调用时,需要对变量i求值,解析程序首先会在事件处理程序内部查找,但i没有定义。然后,又到 badClosureExample()函数中查找,此时有定义,但i的值是4(只有i大于4才会停止执行for循环)。因此,就会取得该值——这正是闭包(匿名函数)要使用其外部函(badClosureExample)作用域中变量的结果。而且,这也是由于匿名函数本身无法传递参数(故而无法维护自己的作用域)造成的。

那么这个例子的问题怎么解决呢?其实方法有很多(自己不妨写一下看看),我认为比较简单直接的代码:

function popNum(oNum){
  return function(){
          alert('您单击的是第'+oNum+'个链接');
  }
}
function badClosureExample(){
  for (var i = 1; i <4; i++) {
    var element = document.getElementById('closureTest' + i);
    element .onclick =new popNum(i);
    }
}

(2)、巧妙利用闭包绑定参数

还是上面的HTML片段,我们要在用户单击第一个链接时延时弹出一个警告框,怎么实现?答案是使用setTimeout()函数,这个函数会在指定的毫秒数之后调用一个函数,如:

setTimeout(someFunc,1000);

但问题是,无法给其中的someFunc函数传递参数。而使用闭包则可以轻松解决这个问题:
function goodClosureExample(oMsg){
  return function(){
    alert(oMsg);
  };
}

函数goodClosureExample用来返回一个匿名函数(闭包)。而我们可以通过为它传递参数来使返回的匿名函数绑定该参数,如:

var good = goodClosureExample('这个参数是通过闭包绑定的');

而此时,就可以将绑定了参数的good函数传递给setTimeout()实现延时警告了:
setTimeout(good,1000) //此时good中已经绑定了参数

最后,测试通过的完整代码:
window.onload = function(){
  var element = document.getElementById('closureTest0');
  if (element) {
    var good = goodClosureExample('这个参数是由闭包绑定的');
    element.onclick = function(){
      setTimeout(good, 1000); //延迟1秒弹出提示
    }
  }
}

3、javascript的垃圾回收原理

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

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

在js中使用闭包,往往会给javascript的垃圾回收器制造难题。尤其是遇到对象间复杂的循环引用时,垃圾回收的判断逻辑非常复杂,搞不好就有内存泄漏的危险,所以,慎用闭包。ms貌似已经不建议使用闭包了。

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
获取任意Html元素与body之间的偏移距离 offsetTop、offsetLeft (For:IE5+ FF1 )[
Dec 22 Javascript
JS实现点击链接取消跳转效果的方法
Jan 24 Javascript
js 获取时间间隔实现代码
May 12 Javascript
使用JS轻松实现ionic调用键盘搜索功能(超实用)
Sep 06 Javascript
jquery对所有input type=text的控件赋值实现方法
Dec 02 Javascript
AngularJS控制器controller给模型数据赋初始值的方法
Jan 04 Javascript
JS实现点击下拉菜单把选择的内容同步到input输入框内的实例
Jan 23 Javascript
基于jQuery ztree实现表格风格的树状结构
Aug 31 jQuery
vue中组件的过渡动画及实现代码
Nov 21 Javascript
函数式编程入门实践(一)
Apr 20 Javascript
如何利用JavaScript编写更好的条件语句详解
Aug 10 Javascript
VUE实现吸底按钮
Mar 04 Vue.js
详解JavaScript的流程控制语句
Nov 30 #Javascript
详解JavaScript的表达式与运算符
Nov 30 #Javascript
Bootstrap每天必学之进度条
Nov 30 #Javascript
javascript省市区三级联动下拉框菜单实例演示
Nov 29 #Javascript
jQuery插件EasyUI校验规则 validatebox验证框
Nov 29 #Javascript
实例解析jQuery插件EasyUI最常用的表单验证规则
Nov 29 #Javascript
整理Javascript流程控制语句学习笔记
Nov 29 #Javascript
You might like
在线竞拍系统的PHP实现框架(二)
2006/10/09 PHP
实用PHP会员权限控制实现原理分析
2011/05/29 PHP
PHP+mysql实现的三级联动菜单功能示例
2019/02/15 PHP
JavaScript 无符号右移运算符
2009/04/17 Javascript
JavaScript闭包 懂不懂由你反正我是懂了
2011/10/21 Javascript
JavaScript中判断对象类型的几种方法总结
2013/11/11 Javascript
JQuery文字列表向上滚动的代码
2013/11/13 Javascript
如何使用jquery easyui创建标签组件
2015/11/18 Javascript
AngularJS通过$sce输出html的方法
2016/09/22 Javascript
Bootstrap table使用方法详细介绍
2016/12/09 Javascript
关于javascript事件响应的基础语法总结(必看篇)
2016/12/26 Javascript
简单易懂的天气插件(代码分享)
2017/02/04 Javascript
微信小程序自定义toast弹窗效果的实现代码
2018/11/15 Javascript
vue实现双向绑定和依赖收集遇到的坑
2018/11/29 Javascript
浅析Proxy可以优化vue的数据监听机制问题及实现思路
2018/11/29 Javascript
关于JS解构的5种有趣用法
2019/09/05 Javascript
Antd表格滚动 宽度自适应 不换行的实例
2020/10/27 Javascript
简单的Python2.7编程初学经验总结
2015/04/01 Python
编写Python脚本来获取Google搜索结果的示例
2015/05/04 Python
基于DataFrame筛选数据与loc的用法详解
2018/05/18 Python
python操作openpyxl导出Excel 设置单元格格式及合并处理代码实例
2019/08/27 Python
基于python生成英文版词云图代码实例
2020/05/16 Python
python中的unittest框架实例详解
2021/02/05 Python
互动出版网:专业书籍
2017/03/21 全球购物
美国领先的商务贺卡出版商:The Gallery Collection
2018/02/13 全球购物
Audible英国:有声读物,30天免费试用
2019/10/16 全球购物
采购主管的岗位职责
2013/12/17 职场文书
给分销商的致歉信
2014/01/14 职场文书
责任担保书范文
2014/05/21 职场文书
关爱残疾人演讲稿
2014/05/24 职场文书
2014年环保局工作总结
2014/12/11 职场文书
董事长秘书岗位职责
2015/02/13 职场文书
大学学生会主席竞选稿怎么写?
2019/08/19 职场文书
centos8安装MongoDB的详细过程
2021/10/24 MongoDB
详解Oracle数据库中自带的所有表结构(sql代码)
2021/11/20 Oracle
MySQL之MyISAM存储引擎的非聚簇索引详解
2022/03/03 MySQL