前言: 想要了解闭包,首先要了解另一篇的文章。
1.闭包是什么?
内部函数在定义的词法作用域外被调用,就会产生闭包。
定义时所在的作用域不被垃圾回收机制自动回收。仍然保留在内存中。
缺点:
内部函数在外面每次被调用的时候,都会产生一个新的闭包。闭包导致大量内存不能被js引擎的垃圾回收机制释放。
从而造成网页性能问题。
解决办法:
使用块作用域。
上图中wait()执行后,相当于在timer的词法作用域之外调用timer函数。timer函数的词法作用域在setTimeout中,被wait创建的词法作用域嵌套。
timer函数可以访问到wait()作用域中的变量和内容。wait()作用域对于外部作用域来说是封闭的。timer函数有个涵盖wait(...)的作用域闭包。
⚠️IIFE虽然不是闭包,但是它具有闭包的性质。
2. 如何解决闭包内存不被回收的问题
function process(data) { // process}var someReallyBigData={ /*data*/};process(someReallyBigData);var btn = document.getElementById('my_button');btn.addEventListener("click", function click(evt) { console.log('==clicked==');}, /*capture=*/false);// click函数是监听事件的回调,它拥有涵盖全局作用域的闭包。 // 正常process执行完成后,后面不再使用占用大量内存数据结构someReallyBigData,可以被回收掉。// 但是因为闭包不可回收,它还会继续保存这个数据结构。 /********************解决方案*************************** function process(data) { // process}{ // 因为不在全局作用域中,完事就可以被销毁 var someReallyBigData={ /*data*/}; process(someReallyBigData);}var btn = document.getElementById('my_button');btn.addEventListener("click", function click(evt) { console.log('==clicked==');}, /*capture=*/false);
3. 循环和闭包
其实在定时器,事件监听,ajax请求等任务中的回调函数,其实都是在使用闭包。
for(var i =1; i<=5; i++) { setTimeout(function timer() { console.log(i); }, 1000 * i);}// 这段代码本来想输入1-5,每秒一次,每次一个值// 实际情况是每秒输出一个值6,输入了5次。/**按照词法作用域分析,代码等同于下面*****/var i = 6;setTimeout(function timer() { console.log(i);}, 1000);setTimeout(function timer() { console.log(i);}, 2000);setTimeout(function timer() { console.log(i);}, 3000);setTimeout(function timer() { console.log(i);}, 4000);setTimeout(function timer() { console.log(i);}, 5000);因为timer是回调函数,所以,console中的i是最后获取。
如果想要达到最初的目的,则需要每次取值的时候从自己的作用域取值,每次迭代的IIFE函数都有自己的作用域,最后取值的时候从各自的作用域取值。
for(var i = 1; i<=5;i++) { (function IIFE(){ var j = i; setTimeout(function timer() { console.log(j); }, 1000*j); })()}// 等同于for(var i = 1; i<=5;i++) { (function IIFE(j){ setTimeout(function timer() { console.log(j); }, 1000*j); })(i)}
let实现的闭包块作用域;每次迭代都是一个新的作用域。
for(var i = 1; i<=5;i++) { let j = i; // 闭包的块作用域 setTimeout(function timer() { console.log(j); }, 1000*j); }// 等同于for(let i = 1; i<=5; i++) { setTimeout(function timer() { console.log(i); }, 1000*i); }
4. 闭包应用