JavaScript(7):闭包

  在百度百科中,有闭包的解释。
  【百度百科】官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
  【百度百科】闭包的特点:
  1.作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
  2.一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
  百度百科这么说有点绕,感觉意思也差不多,通俗地理解:
  1、闭包就是一个封闭的包,它有对外(环境)变量的引用;
  2、闭包一直存在于内存当中。
  就这两点的理解就足够了,但由此可以有很多的应用,JavaScript语言的魅力也可以在此展现。
  一个简单的例子就很能说明。
  每篇CSDN的文章下面都有点赞按钮和踩的按钮,一般的写法是:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript中的闭包</title>
</head>
<body>
	<label>点赞:</label><label id="lblPraise"></label>
	<label>踩:</label><label id="lblTread"></label>
	<br>
	<button type="button" onclick="praise()">点赞</button>
	<button type="button" onclick="tread()">踩</button>	
	<script>
		praiseCount=0;//点赞的计数
		treadCount=0;//踩的计数
		function praise(){
			praiseCount=praiseCount+1;
			document.getElementById("lblPraise").innerHTML=praiseCount;
		}
		function tread(){
			treadCount=treadCount+1;
			document.getElementById("lblTread").innerHTML=treadCount;			
		}
	</script>
</body>
</html>

  这是可以实现的:

JavaScript(7):闭包

   问题是这里声明了全局变量,容易造成变量污染和冲突,通常情况下是要避免的,那么要实现这个功能,应该怎样写呢?
  把变量声明写在函数内行不行?如果这样的话,每次点击都会从0开始计数了,肯定不行。
  这里就可以利用上面说的闭包的两个特点来实现。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript中的闭包</title>
</head>
<body>
	<label>点赞:</label><label id="lblPraise"></label>
	<label>踩:</label><label id="lblTread"></label>
	<br>
	<button type="button" onclick="btnPraise();console.log(praiseCount);">点赞</button>
	<button type="button" onclick="btnTread();console.log(treadCount);">踩</button>	
	<script>
		var btnPraise=(function(){
		var praiseCount=0;//点赞的计数
		return function praise(){
			praiseCount=praiseCount+1;
			document.getElementById("lblPraise").innerHTML=praiseCount;
		};
		})()
		var btnTread=(function(){
		var treadCount=0;//踩的计数
		return function tread(){
			treadCount=treadCount+1;
			document.getElementById("lblTread").innerHTML=treadCount;
		};
		})()
	</script>
</body>
</html>

  实现效果:

JavaScript(7):闭包

   这里的praiseCount和treadCount在外面是访问不了的,所以console.log(praiseCount);和console.log(treadCount);会报错。
  这个例子就很明显地说明了闭包的作用和特点。
  1、里面是可以访问外面的,但是外面访问里面必须通过外露的方法来实现;
  2、闭包是驻留内存的,而一般函数如果没有引用执行完毕后就作为垃圾回收了。
  这里需要弄清楚的是每个闭包都会牵扯到链式作用域的问题。
  所谓的链式作用域就是在这个作用区域或者范围内,包裹在最内层的对象可以一层一层向上(或者说向外)访问父对象,也就是里面的对象可以访问外面的对象,但外面的对象是不能访问里面的对象。
  通过例子可以很清楚地表现出这个概念。

	<script>
	var a=1;
	function funA(){
		var a=2;
		return function funB(){
			var a=3;
			return function funC(){
				return a;
			}
		}
	}
	
	var x=funA;
	console.log(x()()());
	</script>

  上面的输出为3。
  如果是:

	<script>
	var a=1;
	function funA(){
		var a=2;
		return function funB(){
			return function funC(){
				return a;
			}
		}
	}	
	var x=funA;
	console.log(x()()());
	</script>

  上面的输出为2。
  如果是:

	<script>
	var a=1;
	function funA(){
		return function funB(){
			return function funC(){
				return a;
			}
		}
	}	
	var x=funA;
	console.log(x()()());
	</script>

  上面的输出为1。
  也就是它会一层一层地寻找这个变量所对应的值,找到了就输出,不再继续寻找,如果一直到最外层也找不到则会报错。
  利用闭包的这两个特点,在JavaScript面向对象的编程中可以有很多的灵活运用,比如对象的封装与继承、对外接口的定义、任务列表对象的迭代等等。

上一篇:JavaScript面试题集(六)


下一篇:js的节点属性详细介绍