在一个函数的环境中,闭包 = 函数 + 词法环境

function m() {
    var a = 1;
    function sub() {}
}

垃圾是不再需要的东西,但是 JS 无法知道你哪些需要哪些不需要,因此 JS 会将无法触达的东西定义为垃圾(相当于 JS 回收的垃圾是所有垃圾的一个子集)

let nums = [1, 2, 3, 4, 5]
nums = [6, 7, 8, 9, 10]
const sum = nums.reduce((total, num) => total + num)
console.log(sum)

此时第一个数组就会被 JS 判定为垃圾(你没有任何机会访问到它了,无法触达),直接回收

let nums = [1, 2, 3, 4, 5]
const sum = nums.reduce((total, num) => total + num)
console.log(sum)

此时,JS 不知道你以后还会不会用 nums,所以不会回收

如果你以后都不用 nums 了(被你定性为垃圾),那么就会造成内存泄漏

仍可触达,但不再需要了

此时可以手动将该数组改为无法触达

let nums = [1, 2, 3, 4, 5]
const sum = nums.reduce((total, num) => total + num)
console.log(sum)
nums = null

结合闭包:

function createIncrease() {
    const doms = new Array(100000).fill(0).map((_, i) => {
        const dom = document.createElement('div')
        dom.innerHTML = i
        return dom
    })
    function increase() {
        doms.forEach((dom) => {
            dom.innerHTML = Number(dom.innerHTML) + 1
        })
    }
    return increase
}

const increase = createIncrease();
const btn = document.querySelector('button')
function handleClick() {
    increase()
    btn.removeEventListener('click', handleClick)
    increase = null
}
btn.addEventListener('click', handleClick)

此时如果掉以轻心,不把 increase = null,就会因为闭包环境关联的 doms 造成内存泄漏

总结一下,闭包什么情况下会造成内存泄漏?

  1. 持有了不再需要的函数引用,导致函数关联的词法环境无法销毁
  2. 当多个函数共享词法环境时,会导致词法环境膨胀,从而导致出现无法触达也无法回收的内存

    function createIncrease() {
        const doms = new Array(100000).fill(0).map((_, i) => {
            const dom = document.createElement('div')
            dom.innerHTML = i
            return dom
        })
        function increase() {}
        function _temp() {
            doms
        }
        return increase
    }
    
    const increase = createIncrease();
    const btn = document.querySelector('button')
    btn.addEventListener('click', () => {
        increase = createIncrease()
    })

    此时 doms 是一块无法触达的内存空间,但又无法回收

    因为 increase_temp 的词法环境一样,_temp 要用到 doms,所以垃圾回收器不会销毁