ES6 let解决的闭包问题

1,043 阅读3分钟

ES6 let

let 是 ES6 新增的命令,用来声明变量

let 用法和 var 类似,区别:
  • var 声明变量存在变量提升,let 不存在变量提升
  • var 可以重复声明变量,let 不可以重复声明
  • let 声明变量存在块作用域({} 内)

js 闭包

闭包 就是能够读取其他函数内部变量的函数。由于 js 作用域链,只有函数内部的子函数才能读取函数内部的局部变量,所以可以把闭包简单理解成"定义在一个函数内部的函数",本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

闭包用途:
  • 使函数外部能够读取函数内部的变量
    function fn() {
        var num = 1
        //闭包:getNum函数通过作用域链可以访问到变量num,并将其返回到fn函数外部
        return function getNum() {
            return num
        }
    }
    
    //变量num为fn函数的局部变量,函数外面无法访问
    console.log(num)   //报错:num is not defined
    
    //getNum函数作为fn函数的返回值被赋值给n,执行n返回num
    var n = fn()
    console.log(n())   //1
    
  • 让函数内部变量的值始终保存在内存中
    function add() {
        var num = 1
        //闭包:addNum函数通过作用域链可以访问到变量num,并将其的累加值返回到add函数外部
        return function addNum() {
            return num++
        }
    }
    
    //addNum函数作为add函数的返回值被赋值给全局变量n,执行n返回num的累加值
    //add函数内部引用着add函数里的变量num,所以变量num始终被保存在内存中,不会被释放
    var n = add()
    console.log(n())   //1
    console.log(n())   //2
    console.log(n())   //3
    

使用 let 解决的闭包问题

举例:预期输出 0 1 2 3 4

for(var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i)
    }, 0)
}
实际输出:

在这里插入图片描述

分析:

由于 js 是单线程的,按照 js 的事件循环机制,在执行同步任务 for 循环时,异步任务 setTimeout 定时器被放到任务队列中排队等待执行,等待 for 循环执行完,此时 i 的的值已经为5,任务队列中的五个 setTimeout 开始执行,访问到的 i 的值都为5,所以打印出来五个5

问题:

全局作用域中 var 声明的变量为全局变量,每次循环 i 的值不能被保存,这就会出现闭包问题

使用立即执行函数解决:

for(var i = 0; i < 5; i++) {
    (function(i) {
        setTimeout(function() {
            console.log(i)
        }, 0)
    })(i)
}
输出:

在这里插入图片描述

分析:

在 for 循环中,将 setTimeout 定时器放到一个立即执行函数中,将每次循环 i 的值保存到函数作用域里,作为参数传递给 setTimeout,在执行五个 setTimeout 时访问到的 i 为当前函数作用域里每次循环保存起来的 i 的值,所以打印出来0 1 2 3 4

总结:

利用函数作用域形成闭包,保存每次循环 i 的值

使用 let 解决:

for(let i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i)
    }, 0)
}
输出:

在这里插入图片描述

分析:

将 for 循环中的 var 改为 let,这样在每次 for 循环之前都会生成一个块级作用域,每个块级作用域互不干扰,let 声明的变量 i 只在当前循环的块级作用域中有效,每一次循环的 i 都被声明为一个新的变量,在执行五个 setTimeout 定时器时访问到的 i 为当前块级作用域里声明的 i 的值,所以打印出来0 1 2 3 4

总结:

利用块级作用域形成闭包,保存每次循环 i 的值