一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure),闭包会导致原始作用域链不释放,造成内存泄漏(占用),闭包最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中
闭包的作用
1.访问其他函数内部变量
2.保护变量不被内存回收机制回收
3.避免全局变量被污染 方便调用上下文的局部变量 加强封装性
闭包的使用
1、读取函数内部的变量;
2、这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除
立即执行函数
var add = (function () { var counter = 0; return function () {return counter += 1;} })(); add(); add(); add(); // 计数器目前是 3
let
var arr1 = [];
for(let i = 0;i < 10;i++){ // 注意:这里把 var 换成了 let
arr1[i] = function(){
console.log(i);
}
}
arr1[0](); // 0
arr1[1](); // 1
arr1[2](); // 2
var声明的变量,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变。let声明的变量,仅在块级作用域内有效,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量(每次循环都创建一个新的块级作用域)。而JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
forEach()
//使用forEach() function makeClosures(arr, fn) { var result = new Array(); arr.forEach(function(curr){ result.push(function(){return fn(curr)}); }) return result; }
bind()
//使用ES5的bind()方法
function makeClosures(arr, fn) {
var result = new Array();
for(var i=0;i<arr.length;i++){
result[i] = fn.bind(null,arr[i]);
}
return result;
}
内存泄漏
当然,闭包的作用域链中保存的元素,该元素将无法被销毁,在垃圾回收时不会被收回。如果保存元素为一个引用变量,而且不是必须要保存的,那么它也会因此被保存下来占据大量的内存,造成内存泄漏。所以当闭包作用域链中保存的引用变量不需要的时候,应设置为null,解除引用确保正常回收其占用的内存。
function assignHandler(){
var element = $('id');
var id = elment.id;
element.onclick = function(){
alert(id);
};
element = null;//将闭包引用的外部函数中活动对象清除
}