闭包详解

前言: 想要了解闭包,首先要了解另一篇作用域的文章。

1.闭包是什么?

js文件执行时会产生一个全局执行上下文,函数执行时会产生一个函数执行上下文。上下文创建时会生成变量对象。

如果在本执行上下文值中访问其他执行下文中的变量对象,或者在其他执行上下文中访问本上下文中的变量,都会产生闭包。

产生闭包的执行上下文不会销毁,不被垃圾回收机制自动回收。一直保留在内存中。

缺点:

闭包导致大量内存不能被js引擎的垃圾回收机制释放。从而造成网页性能问题。

解决办法:

使用块作用域。

2. 闭包应用

1. 监听函数和闭包

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);

2. 块级作用域和闭包

其实在定时器,事件监听,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);   
}

3. 函数柯里化

函数柯里化的实现,本质上应用了闭包的概念。在函数作用域内部声明的函数,在函数作用域外执行

function curry(fn, arr=[]) {
  const length = fn.length;
  return function(...args) {
    arr = arr.concat(args);
    if(arr.length < length) {
      return curry(fn, arr);
    }
    return fn(...arr);
  }
}
原文地址:https://www.cnblogs.com/lyraLee/p/11434901.html