函数的闭包

广义的闭包理解: 当一个方法在运行的时候, 就会形成一个私有的作用域, 在这个作用域里, 里面定义的变量不会受到上一级作用域或其它作用域的影响, 不会和全局或其它作用域里的变量有冲突. 这个由方法运行而产生的私有作用域, 就叫闭包. 总之, 闭包其实就是函数在运行的时候产生的那个私有作用域.

更直白一些就是为了让变量更安全, 让一个环境中的变量与其它环境中的变量隔离开不产生冲突.

var name = 'Sean';
var age = 8;
function fn(personName, personAge){   
var name = 'Nicole';
var age = 4;	
alert(name + '有' + age + '岁大了, ' + personName + '有' + personAge + '岁大了');
}
fn(name,age);

在这个例子里, 定义了两个全局变量name和age, 在下面的匿名函数里也定义了变量name和age,但这两组相同的变量互不影响, 匿名方法里的变量属于自己的私有的作用域, 这个运行的匿名方法就形成了闭包. 如果在这个匿名方法想用到全局变量name和age,可以通过传参数的方式传给这个匿名函数.

而闭包的简单理解是这样的: 闭包是一种具有状态的函数, 其相关的局部变量在函数调用结束以后仍然继续存在; 当内部函数在定义它的作用域外部被引用时,就创建了该内部函数的一个闭包.

var fn = function() {
    var a = 1;
    return function() {
	    a++;
	    alert(a);
     }
};
var f = fn();
f(); // 2
f(); // 3
f(); // 4
f(); // 5

function a() {
    var a = 1;
    return function() {
        a++;
        console.log(a);
        }
    }
var b = a();
b();
b();
b();

var b = (function() {
	var a = 1;
	return function() {
		a++;
		console.log(a);
	}
})();
b();
b();
b();

一般来说, 函数执行完毕后, 函数内的局部变量会随着函数的调用结束而销毁, 但上面的代码, 在退出函数后, 局部变量a并没有消失. 那是因为执行var f = fn();时, func函数内的匿名函数的引用被返回给了f, 它可以访问到fn()被调用时产生的环境, 局部变量a所在的环境还需要被外界访问, 这样a就不会被被销毁.从本质上讲, 如果内部函数引用了位于外部函数中的变量, 相当于授权该变量能被延迟使用. 因此, 当外部函数调用完成后, 这些变量的内存不会被释放, 因为闭包仍然需要使用它们

闭包使一些数据无法及时销毁,有说法是可能会造成内存泄漏. 不过无须过度担心. 放在闭包中不销毁的变量和放在全局作用域的变量对内存的影响是一致的,不是内存泄露. 如果需要回收, 手动把这些变量设为null即可.

下面是个闭包的常见应用: 页面上有5个div元素,通过循环给每个div绑定onclick事件, 按照索引顺序, 点击第1个div时弹起索引0, 第二个弹起索引1,以此类推.

<body>
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
    <script>
       var nodes = document.getElementsByTagName('div');
       for (var i=0; i<nodes.length; i++) {  
     	nodes[i].onclick = function() {  // 这里的这个匿名方法,在循环运行的时候匿名方法本身并不运行,当点击某个div的时候才会运行
   		    alert(i); // 这里的i定义在上一级作用域里, onclick事件触发的时候循环已经结束, i的值已经等于nodes.length了
     	}
       }
</script>
</body>

上述代码, 无论点击哪个div, 最后弹出的结果都是5. 这是因为onclick事件被触发的时候, for循环已经结束, 此时变量i的值是5; 所以div的onclick事件函数顺着作用域链查找到变量i的时候, 值总是5.

解决方法就是在用闭包的方式改写for循环, 将每次循环获得的i值都封闭起来. 时间函数顺着作用域链从内而外查找变量i时, 会先找到封闭在闭包环境里的i.

for (var i=0; i<nodes.length; i++) {
  (function(i) {
    nodes[i].onclick = function() {
      alert(i);
   	}})(i);
   }

或者这样:

for (var i=0; i<nodes.length; i++) {
  nodes[i].onclick = (function(i) {
    return function() {
      alert(i);
     }
  })(i);
}

不过这个问题也可以通过自定义属性的方式解决

for (var i=0; i<nodes.length; i++) {
    nodes[i].newIndex = i
   	nodes[i].onclick = function() {
        alert(this.newIndex);
   	}
}
posted @ 2015-07-22 19:53  supersylph  阅读(257)  评论(0编辑  收藏  举报