JS中经典的闭包

131 阅读3分钟

闭包是JavaScript中老生常态的话题,如何理解闭包,以及该怎样使用,下面就跟我一起来看看吧。

概念

  • 红宝书:闭包是指有权访问另一个函数作用域中变量的函数
  • MDN: 闭包是指那些能够访问自由变量的函数,这里的自由变量是指外部作用域中的变量
  • 个人理解:闭包 = 内层函数 + 引用外层函数变量

先来看个例子:

function out() {
    const a = 1;
    function fn() {
        console.log(a);
    }
    fn()
}
out()

从这个例子可以得出,闭包一定有return吗?闭包一定会内存泄漏吗?当然不是的,接着往下看。

闭包中什么时候用到return呢?

外部想要使用闭包的变量,则需要return

function out() {
    const a = 1;
    return function (){
        console.log(a);
    }
}
const fn = out();
fn()

return的目的是把内部的局部变量返回到外面来,让外面也可以使用这个值

闭包的好与坏

  • 闭包的好处:

    • 变量长期驻扎在内存中
    • 避免污染全局变量
    • 私有成员的存在
  • 闭包的坏处:

    • 增大内存的使用量
    • 容易造成内存泄漏

实现数据私有

let i = 1;
function fn() {
    i++;
    console.log(`调用次数${i}`);
}
fn()

这里i是一个全局变量,很容易就被修改,如果对i进行修改,影响调用的次数,这个时候就可以使用闭包的形式“私有化”

function fn() {
    let i = 1;
    function count() {
        i++;
        console.log(`调用次数${i}`);
    }
    return count
}
const res = fn()
res()

这样就实现了“私有化” 不能直接修改i的值了

造成内存泄漏的操作

先来说说什么是内存泄漏和垃圾回收器

  • 内存泄露:当已经不需要某块内存时这块内存还存在着,没有被释放,导致该内存无法被使用
  • 垃圾回收器:执行环境负责管理代码执行过程中使用的内存。JS的垃圾回收机制是为了以防内存泄漏

造成泄漏的操作

  • 意外的变量:变量引用、变量未申明。当变量使用不当,没有及时回收(手动赋值 null
  • 计时器或回调函数
    • 当不需要 Interval 或者 timeout 时,最好调用 clearInterval 或者 clearTimeout来清除
function out () {
    var name = 'Jack'
    var age = 20

    function bar () {
        console.log(name)
        console.log(age)
    }
    return bar
}
var fn = out()
fn()
fn = null // 阻止内存泄漏

fn函数调用完毕之后,out函数会自动销毁,但out函数中的变量name和age不会被销毁,因为在bar函数内部进行了访问,并且根据垃圾回收机制,被另一个作用域引用的变量不会被回收。除非bar函数解除调用才能销毁。 如果该函数使用的次数很少,不进行销毁的话就会变为闭包产生的内存泄漏。

window.onload = function(){
    var el = document.getElementById("id");
    var id = el.id;  //解除循环引用
    el.onclick = function(){
        alert(id); 
    }
    el = null;  // 将闭包引用的外部函数中活动对象清除
}

退出函数之前,将不使用的局部变量全部删除,可以使变量赋值为null

function setCallback() {
    let counter = 0;
    return function cb() {
        counter++; // 只有计数器是回调范围的一部分
        console.log(counter);
    }
}
const timer = setInterval(setCallback(), 1000);
clearInterval(timer); // 停止计时器,比如如果按下按钮

引用setInterval没有被清除,它自身不会被清除需要手动clearInterval