1.什么是闭包?
W3C:闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。
要较好理解闭包,除了形式本身,还应先理解:
JS没有块级作用域:见JavaScript作用域;
JS的内存回收机制:引用计数。
2.闭包的例
闭包是一种结构,getName引用了外部变量name,形成闭包。第9行把name赋给变量whoname,使name在内存中保持。如果没有getName形成闭包,People函数执行完,其name变量就被释放。
1 function People(){ 2 var name = ‘xiaohua‘; 3 function getName(){ 4 return name; 5 } 6 return getName; 7 } 8 9 var whoName = new People();
3.闭包容易引起的bug
var result = []; function foo(){ var i = 0; for(;i<3;i++){ result[i] = function(){ alert(i); } } } foo(); result[0](); //3 result[1](); //3 result[2](); //3
这是因为i始终存在,没有块级作用域,i最后等于3,result数组中的所有函数里的i都指向那个等于3的i。
解决办法是给函数传参数:
1 result[i] = function(num){ 2 alert(num); 3 }
这种做法的原理涉及到作用域链。作用域链依次指向由近到远的作用域对象,上面给匿名函数添加一个参数,其最近作用域对象上就会有这个参数,参数是值类型,传递的只是副本,因此解决了上述问题。
4.闭包的几个用途
- 隔离作用域
JS虽然没有块级作用域,但是有函数作用域。为了使变量之间不会有命名冲突,使用立即执行函数把作用域隔离。下面的两个作用域内的name互不影响,无意间形成了闭包的结构。如果没有隔离作用域
1 (function(){ 2 var name = ‘aa‘; 3 4 function a(){ 5 return name; 6 } 7 })(); 8 9 (function(){ 10 var name = ‘bb‘; 11 12 function a(){ 13 return name; 14 } 15 })();
- 作计数器
1 var counter = function(){ 2 var count = 0; 3 return function(){ 4 return ++count; 5 }; 6 }; 7 8 var countAdd = counter(); // function(){ return ++count; }; 9 countAdd(); //1 10 countAdd(); //2 11 alert(countAdd()); //3
- 声明私有变量
JS本身没有私有变量、公共变量,静态变量的,都是模拟实现。下面如果把name作为People的属性,就无法实现私有。
1 var People = (function(){ 2 var name = "xiaohua"; 3 function People(){}; 4 People.prototype = { 5 getName:function(){ 6 return name; 7 }, 8 setName:function(newName){ 9 name=newName; 10 } 11 }; 12 return People; 13 })(); 14 15 var p = new People();
5.闭包的代价
闭包引用外部变量使得外部变量在其作用域执行完毕后不能立即销毁,必须等到闭包函数被销毁,引用清楚之后才能销毁。因此,闭包最明显的代价是占用内存。
其次,复杂闭包容易造成逻辑混乱,进而导致循环引用。循环引用的结果是变量始终被引用,无法释放,造成内存泄漏。
参考:
W3C和饥人谷公开课
时间: 2024-11-25 15:47:49