js闭包经典问题的理解


JS闭包的作用

本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。 闭包可以用在许多地方。 它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。2009年8月30日学习Javascript闭包(Closure) - 阮一峰的网络日志

错误示例

<body>
	<p class="myP">test</p>
	<p class="myP">test2</p>
	<p class="myP">test3</p>
</body>

目的: 输出当前点击节点的序号

var nodes = document.getElementsByClassName('myP');
			
var add_the_handlers = function(nodes){
	var i
	for(var i = 0; i < nodes.length; i++){
		nodes[i].onclick = function(e){
			console.log(i);
		}
	}
}

add_the_handlers(nodes);

输出结果: 节点总数 / 最后一个节点的序号
错误原因: 内部函数在使用外部变量时使用引用的方式而不是复制


引用和复制

在JS中,属于引用数据类型的有 对象、数组、函数,这些数据类型传的是址
属于复制数据类型的有 复制:数字、布尔,这些数据类型传的是值

传值的比较比较的是数值 而传址的比较比较的是引用,引用不同即使数值相同也不等

1 == 1;    		//true
1 === 1;   		//true
[0] == [0];		//false
[0][0] == [0][0];    	//true
[0][0] === [0][0];   	//true
[0].toString() == [0].toString();   //true    

举例说明

var a = 1;
var b = a;   //赋的是a的复制值
b ++;
alert(a);   //"1"   b的修改不影响a

var a = [1];
var b = a;     //赋的是a的引用 
b[0] ++;
alert(a);  //"2"   b的修改对a也有效

函数的参数
传值的传递:传给函数的是数值的一个复制,函数中对其的修改外部不可见

var a = 1;
var b = 2;
function change(a,b) {
  var c = a;
  a = b;      //用新引用覆盖
  b = c;
  alert(a);   //"2"         
  alert(b);   //"1"
}
change(a,b);
alert(a);   //"1"         
alert(b);   //"2"

传址的传递:传给函数的是数值的一个引用,函数中对其属性的修改外部可见,但用新引用覆盖其则在外部不可见

var a = [1, 2, 3];
var b = [5, 6];
function change(a,b) {
  a[0] = 4;    //对其属性的修改外部可见 外部输出结果从1,2,3变为4,2,3
  var c = a;
  a = b;      //用新引用覆盖 外部输出结果不变
  b = c;
  alert(a);   //"5,6"         
  alert(b);   //"4,2,3"
}
change(a,b);
alert(a);    //"4,2,3"
alert(b);     //"5,6"

如果要使上述ab在函数外部互换,就需要通过作用域来实现。


作用域

var a = [1, 2, 3];
var b = [5, 6];
function change() {
  var c = a;
  a[0] = 4;
  a = b;
  b = c;
};
change();
alert(a);   //"5,6"
alert(b);   //"4,2,3"

因为js没有块级作用域,所以它在change里找不到变量a、b,所以会向上级寻找,操作的都是全局变量的引用。


闭包

在搞明白了复制、引用和作用域的概念后,我们就可以去更好的理解闭包了,还是用上面的例子

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<p class="myP">test</p>
		<p class="myP">test2</p>
		<p class="myP">test3</p>
	</body>
	<script>
		window.onload = function(){
			var nodes = document.getElementsByClassName('myP');
			
			// 通过立即执行函数每当i改变前,使内部函数获得外部变量i的复制并绑定事件。
			// 示例1: 通过 匿名函数 立即执行来传递外部变量i
			var add_handlers = function (nodes) {
				var i;
				for (i = 0, l = nodes.length; i < l; i ++) {
					// 接收一个参数i
					// 该参数为数字类型,传进去后是数字的复制,而非引用
					nodes[i].onclick = function (i) { 
						// 返回函数给事件处理器,以实现点击后弹窗的操作
						return function(){
							// 此处的i值是传进来的i,属于函数的私有变量
							alert(i);
						}
					}(i); // 通过(i)来使函数立即执行,同时通过(i)使其获得外部变量i的复制
			    }
			};
				
			// 示例2:通过 辅助函数 来传递外部变量i
			var add_the_handlers = function(nodes){
				var helper = function(i){
					return function(){
						alert(i); // 此处的i是函数形参的值,属于helper函数的私有变量
					}
				}
				
				var i;
				
				for(i = 0; i < nodes.length; i++){
					// 通过立即执行函数helper(i)来将外部变量i的值的复制传递给了helper
					nodes[i].onclick = helper(i)
				}
			}
			
			add_the_handlers(nodes);
		}
	</script>
</html>

通过闭包,我们可以实现从内部函数拿到外部函数的变量。

文章部分案例参考见此

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值