定义:
当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露;
怎样理解内存泄漏? 正常情况函数执行结束就会销毁自己的作用域链,但是由于闭包的出现,内部函数被保存到了外部就还可以使用它的作用域链,即没有立即释放,就会占用过多了内存,内存变少,即内存泄漏。
例:
function test(){
var arr = [];
for(var i = 0; i< 10; i++){
arr[i] = function (){
console.log(i);
}
}
return arr;
}
var newArr = test();
for(var j = 0; j < 10; j++){
newArr[j]();
}
执行结果:10,10,10,10,10,10,10,10,10,10
过程解析:关键点在于数组test函数内的arr[i]=function(){}和test形成闭包,在 var newArr = test();语句执行时并没有执行数组内的函数,仅把此函数的引用抛到全局(在函数没有被执行时,即在定义时js引擎是不会看函数内有什么执行语句),当newArr[j]();语句执行时才执行函数内部语句,再回去寻找变量i,但是由于循环此时的i已经变成了10,所以会打印10个10;
使用立即执行函数优化上述代码:
function test() {
var arr = [];
for (var i = 0; i < 10; i++) {
(function (i) {
arr[i] = function () {
console.log(i);
}
}(i))
}
return arr;
}
var newArr = test();
for (var j = 0; j < 10; j++) {
newArr[j]();
}
执行结果:0,....,9
过程解析:立即执行函数不同于普通函数的一点是,只要读到这个函数就会执行,在test()内部循环时,产生了10个立即执行函数,所以当外部newArr[j]();时,数组内的函数会去寻找它自己对应的那个立即执行函数,i自然也就是这个立即执行函数的参数,所以会依次打印0,...,9
闭包作用:
一、实现不依赖外部变量,并且能反复执行的独立函数模块(公有变量);
//eg:累加器
function add(){
var count = 0;
function demo(){
count ++;
console.log(count);
}
return demo;
}
var counter = add();
counter();//1
counter();//2
counter();//3
counter();//4
执行解析:函数(add)内部函数(demo)被保存到了外部(return demo),由于内部函数(demo)在被定义时继承了父级函数(add)的作用域链,虽然当父级函数执行结束销毁了这个作用域链,仅是销毁了对这个作用域的引用,但内部函数被保存了出来,可以继续使用这个AO,即将父级函数的变量count实现了共有变量(外部可以使用)。
二、可以做类似缓存的效果(缓存:外部不可见,但确实存在的储存结构,可对其进行存取操作)多个函数和一个函数形成闭包,被保存出来的每一个函数共用一个作用链,一个函数修改了这个作用域链中的某个值,其他函数中使用的这个值也会随之修改,这个作用域链就相当于一个储存仓库,下次取值就是更新过的值(类似Vuex的store)。
//做缓存实际应用
function eater(){
var food = '';
var obj = {
eat:function (){
console.log('I am eating ' + food);
food = '';
},
push: function (myFood){
food = myFood;
}
}
return obj;
}
var eater_1 = eater();
eater_1.push('banana');
eater_1.eat();//I am eating banana
三、可以实现封装,属性私有化;
四、模块化开发,防止污染全局变量;
闭包的防范
闭包会导致多个执行函数共用一个公有变量,如果不是特殊需要,应尽量防止这种情况发生