理解闭包概念:
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 引用的时候,就创建了一个闭包。
可能你还是不理解闭包这个概念,因为你不知道闭包有什么用,那么先理解一下闭包的作用吧。
解释:以上面的的例子为例,函数 out 中 i 只有函数 innder 才能访问,而无法通过其他途径访问到,因此保护了 i 的安全性。
在上面的示例中增加了一次函数 ref() 的调用,执行的结果如下:
解释:当函数 out 执行完并且最终退出时,它的局部变量会被 Javascript 的垃圾回收机制回收所占用的资源,也就是局部变量被销毁,但是因为创建了闭包环境,那么内部函数 inner 就会暂时保存外部函数的局部变量上下文环境。不会被垃圾回收机制回收。所以函数 out 中的 i 被复制一份暂时保存下来,这样每次执行 ref(),i 都是自加 1 后 alert 输出 i 的值。当变量 ref 引用内部函数 inner 完成结束后,最后才会被回收机制回收。这只是我对闭包作用的简单初浅理解,不专业也不严谨,但大概意思就是这样,理解闭包需要循序渐进的过程。
相信你看了闭包的作用,对理解什么是闭包是否更明白一些,如果还是很疑惑,那么我就再举几个闭包的经典例子来帮助你理解闭包的概念吧。
问题:假如我们有如下需求请在页面中放 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 的副本,这种机制就是闭包。
使用闭包可以解决此问题,代码做了如下修改:
- var div=document.getElementsByTagName("div");
- for (var i = 0; i < div.length; i++) {
- div[i].onclick=function(n){
- return function(){
- alert(n);//产生闭包,引用外部变量。
- }
- }(i);
- }
结果:
从上面的结果你会发现,使用闭包后,就达到你预期的结果了。
解释:n 是外部函数的值,但是内部函数需要使用这个值,因为产生闭包执行环境,所以返回函数前的 n 被临时驻留在内存中给点击事件使用,简单说就是函数的执行上下文被保存起来,i 生成了多个副本。
示例代码:
- <!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>
运行 结果:
问题:不使用闭包的情况下,不管执行多少次,结果都一样是 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>
运行结果:
上面代码解释:
总结:
相信通过以上是几个闭包示例,你对闭包也有一定的理解了吧。限于本人才疏学浅,对闭包的理解也并不是很透彻,只是理解了一些表面,会使用而已。
若你想理解的更深入推荐你去看老外对闭包的解释:
来源: