JavaScript闭包

278 阅读3分钟

这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战

闭包是什么

做前端的可太需要了解闭包了,几乎每个面试都会问到闭包,闭包的重要性不言而喻。什么是闭包:闭包一般是指那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。

    function f1() {
        var a = 666;

        function f2() {
            console.log(a);
        }
        return f2; // f1返回了f2的引用
    }
    var jackson = f1(); // jackson就是f2函数了
    jackson(); // 执行jackson,全局作用域下没有a的定义,
    //但是函数闭包,能够把定义函数的时候的作用域一起记住,输出666

上面的例子,首先有执行上下文f1,在f1中定义了函数f2,而通过对外返回f2的方式让f2得以执行。当f2执行时,访问了f1内部的变量a。这个时候闭包就产生了。

闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来。这显然类似于面向对象编程。在面向对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。因此,通常你使用只有一个方法的对象的地方,都可以使用闭包。

this对象

闭包中使用this会让代码变得复杂,之前的文章说过this指向问题,每个函数在被调用的时候都会自动创建俩个特殊变量:this和arguments。内部函数不可以直接访问外部函数的这俩个变量,但是可以把this保存到闭包就可以行得通。我们先看一下直接访问。

    window.identity = 'Jackson';
    let obj = {
        identity: 'bear',
        getIdentityFunc() {
            return function () {
                return this.identity;
            };
        }
    };
    console.log(obj.getIdentityFunc()()); // 'Jackson'

看一下把this保存到变量that。

    window.identity = 'Jackson';
    let obj = {
        identity: 'bear',
        getIdentityFunc() {
            let that = this;
            return function () {
                return that.identity;
            };
        }
    };
    console.log(obj.getIdentityFunc()()); // 'bear'

在定义匿名函数之前,先把this保存一下,在定义闭包时,可以让它访问that,这是因为包含函数中名称没有任何冲突的一个变量。即使在外部函数返回后,that仍然指向obj。

因为闭包会保留他们包含的函数作用域,所以它比其他函数更占用内存,过度使用闭包而不释放的话就会导致过度占用。解决方法是,在退出函数之前,将不使用的局部变量全部删除,我们在之前讲过垃圾回收,点击查看(JavaScript的垃圾回收 (juejin.cn))

内存泄漏

在旧版本浏览器中,尤其是ie,如果把html元素保存在闭包的作用域中,就相当于该元素不能被销毁。

    function clickaaa() {
        let element = document.getElementById('aaa');
        element.onclick = () => console.log(element.id);
    }

这里写了一个点击aaa元素,让它输出aaa的id,只要这个函数存在,element的计数就至少等于1,也就是永远不会被回收。想让它回收的话我们在后面把它释放掉就可以了,大家一定要养成良好习惯。

    function clickaaa() {
        let element = document.getElementById('aaa');
        element.onclick = () => console.log(element.id);
        element = null;//释放
    }