一、闭包(Closure)

1.1、什么是闭包?

理解闭包概念:

a、闭包是指有权限访问另一个函数作用域的变量的函数,创建闭包的常见方式就是在一个函数内部创建另一个函数,也就是创建一个内部函数,创建一个闭包环境,让返回的这个内部函数保存要引用的变量,以便在后续执行时可以保持对这个变量的引用。

b、只要存在调用内部函数的可能,JavaScript就需要保留被引用的函数。而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间。

c、Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。

d、如果一个内部函数被调用且引用了它的外部变量那么它就是一个闭包。

相信你看了上面的这段话可能还不理解什么是闭包,那么我就举一个闭包的经典例子来帮助你理解闭包的概念吧。

请看下面这段代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>闭包</title>
</head>
<body>
<script type="text/javascript">
function out() {
var i = 0;
function inner() {
alert(++i);
}
return inner;
}
var ref= out();
ref();
</script>
</body>
</html>

结果:

上面的代码有两个特点:
1、创建两个函数out,inner,函数inner嵌套在函数out内部,也可以说是函数inner是函数out的内部函数;
2、调用函数out返回内部函数inner。
这样在执行完var ref=out()后,变量ref实际上是指向了函数inner,也可以说是引用了函数inner,再执行ref()后就会看到上图弹出一个窗口显示i的值第一次为1。

这段代码其实就创建了一个闭包,为什么?因为函数out外的变量ref引用了函数out内部的函数inner,也就是说:

当函数out的内部函数inner被函数out外的一个变量ref引用的时候,就创建了一个闭包。

可能你还是不理解闭包这个概念,因为你不知道闭包有什么用,那么先理解一下闭包的作用吧。

1.2、为什么要用闭包(作用)?

1.2.1、保护函数内的变量安全。

解释:以上面的的例子为例,函数out中i只有函数innder才能访问,而无法通过其他途径访问到,因此保护了i的安全性。

1.2.2、通过访问外部变量,一个闭包可以暂时保存这些变量的上下文环境,当引用完毕后才会销毁。

在上面的示例中增加了一次函数ref()的调用,执行的结果如下:

解释:当函数out执行完并且最终退出时,它的局部变量会被Javascript的垃圾回收机制回收所占用的资源,也就是局部变量被销毁,但是因为创建了闭包环境,那么内部函数inner就会暂时保存外部函数的局部变量上下文环境。不会被垃圾回收机制回收。所以函数out中的i被复制一份暂时保存下来,这样每次执行ref(),i都是自加1后alert输出i的值。当变量ref引用内部函数inner完成结束后,最后才会被回收机制回收。这只是我对闭包作用的简单初浅理解,不专业也不严谨,但大概意思就是这样,理解闭包需要循序渐进的过程。

相信你看了闭包的作用,对理解什么是闭包是否更明白一些,如果还是很疑惑,那么我就再举几个闭包的经典例子来帮助你理解闭包的概念吧。

1.3、闭包的经典示例

1.3.1、示例一

问题:假如我们有如下需求请在页面中放10个div,每个div写上对应的数字,当点击每一个div时显示索引号,如第1个div显示0,第10个div显示9。

可能你会这样做:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>闭包</title>
<style type="text/css">
div {
width: 50px;
height: 50px;
background: lightcoral;
float: left;
margin: 20px;
font: 15px/50px "microsoft yahei";
text-align: center;
}
</style>
</head>
<body>
<div>div-1</div>
<div>div-2</div>
<div>div-3</div>
<div>div-4</div>
<div>div-5</div>
<div>div-6</div>
<div>div-7</div>
<div>div-8</div>
<div>dvi-9</div>
<div>div-10</div>
<script type="text/javascript">
var divs=document.getElementsByTagName("div");
for (var i=0;i<divs.length;i++) {
divs[i].onclick=function(){
alert(i);
}
}
</script>
</body>
</html>

结果:

从上面的结果你会发现,不管你点击了哪个div,弹出的框div索引总是10,这可能会让你很意外,会产生疑惑,为什么会出现这样的结果呢?

解释:因为点击事件的函数内部使用外部的变量i一直在变化,当我们指定click事件时并没有保存i的副本,这样做也是为了提高性能,但达不到我们的目的,我们要让他执行的上下文保存i的副本,这种机制就是闭包。

使用闭包可以解决此问题,代码做了如下修改:

<script type="text/javascript">
var div=document.getElementsByTagName("div");
for (var i = 0; i < div.length; i++) {
div[i].onclick=function(n){
              return function(){
alert(n);//产生闭包,引用外部变量。
}
}(i);
}
</script>

结果:

从上面的结果你会发现,使用闭包后,就达到你预期的结果了。

解释:n是外部函数的值,但是内部函数需要使用这个值,因为产生闭包执行环境,所以返回函数前的n被临时驻留在内存中给点击事件使用,简单说就是函数的执行上下文被保存起来,i生成了多个副本。

1.3.2、示例二

示例代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
function out() {
var i = 10;
return function inner() {
i++;
alert(i);//引用了外部变量,创建了闭包环境
};
}
//此处为函数调用,第一个括符为调用out方法,第二个括符为调用返回的inner方法。
out()();
</script>
</body>
</html>

运行 结果:

1.3.3、示例三

问题:不使用闭包的情况下,不管执行多少次,结果都一样是3.

示例代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>闭包</title>
</head>
<body>
<script type="text/javascript">
var arr = [1, 2, 3];
var obj = {};
var exp = function() {
for(var i = 0; i < arr.length; i++) {
obj[i] = function() {
console.log(i);
};
}
}
exp();
var fn0 = obj[0];
var fn1 = obj[1];
var fn2 = obj[2];
fn0(); //输出3
fn1(); //输出3
fn2(); //输出3
</script>
</body>
</html>

运行结果:

使用闭包后的示例代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>闭包</title>
</head>
<body>
<script type="text/javascript">
var arr = [1, 2, 3];
var obj = {};
var exp = function() {
for(var i = 0; i < arr.length; i++) {
(function(i) {
obj[i] = function() {
console.log(i);
};
})(i); //i作为参数传给立即调用函数
}
};
exp();
var fn0 = obj[0];
var fn1 = obj[1];
var fn2 = obj[2];
fn0(); //输出0
fn1(); //输出1
fn2(); //输出2
</script>
</body>
</html>

运行结果:

上面代码解释:

  • 在for循环里创建了一个立即调用函数表达式
  • fn0,fn1,fn2分别指向了匿名函数的引用
  • fn0(),fn1(),fn2()都访问了i(这个i是位于这个匿名函数的上层作用域链,它会被保存在内存中,对于每一个函数引用来说i是唯一的)

总结:

相信通过以上是几个闭包示例,你对闭包也有一定的理解了吧。限于本人才疏学浅,对闭包的理解也并不是很透彻,只是理解了一些表面,会使用而已。

若你想理解的更深入推荐你去看老外对闭包的解释:http://stackoverflow.com/questions/111102/how-do-javascript-closures-work

作者: 欲泪成雪
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。
 

我所理解的JavaScript闭包的更多相关文章

  1. JavaScript学习总结——我所理解的JavaScript闭包

    一.闭包(Closure) 1.1.什么是闭包? 理解闭包概念: a.闭包是指有权限访问另一个函数作用域的变量的函数,创建闭包的常见方式就是在一个函数内部创建另一个函数,也就是创建一个内部函数,创建一 ...

  2. javascript闭包理解

    //闭包理解一 function superFun(){ var _super_a='a'; function subfuc(){ console.log(_super_a); } return su ...

  3. javascript 闭包最简单理解

    首先说3点与闭包有关系的东西. 一.变量的作用域 变量的作用域不难理解. 1.函数内部可以访问函数外部的变量,而函数外部不能访问函数内部的变量. 2.如果在函数内定义变量的时候,不加var,那么是全局 ...

  4. 全面理解Javascript闭包和闭包的几种写法及用途

    好久没有写博客了,过了一个十一长假都变懒了,今天总算是恢复状态了.好了,进入正题,今天来说一说javascript里面的闭包吧!本篇博客主要讲一些实用的东西,主要将闭包的写法.用法和用途.  一.什么 ...

  5. 深入理解JavaScript闭包(closure)

    最近在网上查阅了不少javascript闭包(closure)相关的资料,写的大多是非常的学术和专业.对于初学者来说别说理解闭包了,就连文字叙述都很难看懂.撰写此文的目的就是用最通俗的文字揭开Java ...

  6. 对于 Javascript 闭包理解

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...

  7. javascript闭包的理解

    闭包是Javascript的一个难点,但也是一个很重要的知识点. 1.首先我们要知道变量作用域链 变量的作用域分两种:全局变量和局部变量.没有定义到任何函数中的变量为全局变量,在函数中定义的变量为局部 ...

  8. 深入理解javascript闭包(一)

    闭包(closure)是Javascript语言的一个难点.也是它的特色,非常多高级应用都要依靠闭包实现. 一.什么是闭包? 官方"的解释是:闭包是一个拥有很多变量和绑定了这些变量的环境的表 ...

  9. 深入理解javascript闭包(一)

    原文转自脚本之家(http://www.jb51.net/article/24101.htm) 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. ...

随机推荐

  1. UVALive 5010 Go Deeper 2sat

    二分答案,2sat判定. //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio& ...

  2. 20145225唐振远 实验二 "Java面向对象程序设计"

    20145225<Java程序设计> 实验二 Java面向对象程序设计 实验报告 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S ...

  3. 水果项目第1集-想法>需求->功能->数据库设计->类设计

    懒,懒人,我是个懒人. 懒人想做点事,总是拖拖拉拉,迟迟没有开始. 很久很久以前,就想做属于自己的产品,但是至今还没有一个属于自己的产品. 两年前,终于想好,要做一个网上卖水果的系统,手机上点点,水果 ...

  4. UITableViewCell重用的问题

    UITableView中有两种重用Cell的方法: - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier; - (id)dequ ...

  5. zabbix告警“Zabbix poller processes more than 75% busy”

    告警原因: 1.某个进程卡住了, 2.僵尸进程出错,太多,导致慢了 3.网络延迟(可忽略) 4.zabbix消耗的内存多了 告警危害: 普通告警,暂无危害(但是最好处理) 处理方法: 一:简单,粗暴( ...

  6. Robot Framework-Windows版本安装

    Robot Framework-Mac版本安装 Robot Framework-Windows版本安装 Robot Framework-工具简介及入门使用 Robot Framework-Databa ...

  7. codeforces 429D

    题意:给定一个数组你个数的数组a,定义sum(i, j)表示sigma(a[i],...a[j]),以及另外一个函数f(i, j) = (i - j)^2 + sum(i+1, j)^2 求最小的f( ...

  8. Swift闭包概念与常见使用场景总结

    ·Swift 闭包 闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值. Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些 ...

  9. 入门级:怎么使用C#进行套接字编程(一)

    翻译一篇简单的文章学习下基础,此文针对我等对socket只听说未尝试阶段的水平. How to C# Socket programming C#通过他的命名空间像System.Net和System.N ...

  10. 单元测试 Mocking 类库需具备的特性

    一个优秀的单元测试 Mocking 类库,需要具备如下几个特性: 易用性:有非常明确的 API ,易于使用并易于记忆. 健壮性:行为结果始终一致,并保持准确. 帮助性:当程序出错时,给出尽可能明确的原 ...