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 相关文章推荐
JS获取IUSR_机器名和IWAM_机器名帐号的密码
Dec 06 Javascript
javascript 必知必会之closure
Sep 21 Javascript
jQuery插件expander实现图片翻转特效
May 21 Javascript
JavaScript通过Date-Mask将日期转换成字符串的方法
Jun 04 Javascript
JQuery在循环中绑定事件的问题详解
Jun 02 Javascript
Bootstrap实现带动画过渡的弹出框
Aug 09 Javascript
jquery+ajax实现省市区三级联动效果简单示例
Jan 04 Javascript
js实现文字无缝向上滚动
Feb 16 Javascript
bootstrap-Treeview实现级联勾选
Nov 23 Javascript
微信小程序自定义toast弹窗效果的实现代码
Nov 15 Javascript
jQuery实现input输入框获取焦点与失去焦点时提示的消失与显示功能示例
May 27 jQuery
浅谈layui数据表格判断问题(加入表单元素),设置单元格样式
Oct 26 Javascript
详解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程序员面试 切忌急功近利(更需要注重以后的发展)
2010/09/01 PHP
浅析php中常量,变量的作用域和生存周期
2013/08/10 PHP
PHP在引号前面添加反斜杠(PHP去除反斜杠)
2013/09/28 PHP
yii框架通过控制台命令创建定时任务示例
2014/04/30 PHP
PHP框架Laravel中使用UUID实现数据分表操作示例
2018/05/30 PHP
thinkPHP5框架闭包函数与子查询传参用法示例
2018/08/02 PHP
PHP使用PDO创建MySQL数据库、表及插入多条数据操作示例
2019/05/30 PHP
javscript对象原型的一些看法
2010/09/19 Javascript
禁用页面部分JavaScript方法的具体实现
2013/07/31 Javascript
JS中使用sort结合localeCompare实现中文排序实例
2014/07/23 Javascript
node.js中的fs.appendFileSync方法使用说明
2014/12/17 Javascript
javascript Array 数组常用方法
2015/04/05 Javascript
Angular2中Bootstrap界面库ng-bootstrap详解
2016/10/18 Javascript
Javascript 使用ajax与C#获取文件大小实例详解
2017/01/13 Javascript
JavaScript截屏功能的实现代码
2017/07/28 Javascript
解决layer弹层遮罩挡住窗体的问题
2018/08/17 Javascript
AngularJs返回前一页面时刷新一次前面页面的方法
2018/10/09 Javascript
解决Idea、WebStorm下使用Vue cli脚手架项目无法使用Webpack别名的问题
2019/10/11 Javascript
使用Vue实现调用接口加载页面初始数据
2019/10/28 Javascript
js回调函数原理与用法案例分析
2020/03/04 Javascript
深入理解javascript中的this
2021/02/08 Javascript
Python实现在Linux系统下更改当前进程运行用户
2015/02/04 Python
Python 3.x 新特性及10大变化
2015/06/12 Python
Python编程求质数实例代码
2018/01/31 Python
Python匿名函数及应用示例
2019/04/09 Python
教你如何编写、保存与运行Python程序的方法
2019/07/12 Python
python读取ini配置的类封装代码实例
2020/01/08 Python
解决Django部署设置Debug=False时xadmin后台管理系统样式丢失
2020/04/07 Python
Expedia印度尼西亚站:预订酒店、廉价航班和度假套餐
2018/01/31 全球购物
如何高效率的查找一个月以内的数据
2012/04/15 面试题
公司授权委托书
2014/04/04 职场文书
分公司任命书
2014/06/06 职场文书
单位授权委托书范本
2014/09/26 职场文书
学雷锋献爱心活动总结
2015/05/11 职场文书
财务人员入职担保书
2015/09/22 职场文书
PHP实现考试倒计时功能代码
2021/04/16 PHP