什么是闭包?闭包的用途是什么?闭包的缺点是什么?

闭包(closures)是 Javascript 语言的一个难点,也是它的特色,很多高级应用都是依靠闭包实现的。闭包与变量的作用域以及变量的生命周期密切相关。

什么是闭包

所谓闭包,指的就是一个函数。当两个函数彼此嵌套时,内部的函数就是闭包。

因为在 JavaScript 中,函数属于对象,对象又是属性的集合,而属性的值又可以是对象,所以我们可以在函数内部再定义函数。例如在函数 A 中定义了函数 B,然后在函数外部调用函数 B,这个过程就是闭包。

闭包的形成条件是内部函数需要通过外部函数 return 给返回出来,如下例所示:

function funOne(){    // 外部函数
    var num = 0;      // 局部变量
    function funTwo(){   // 内部函数
        num++;                 
        return num;
    }
    return funTwo;
}
var fun = funOne();             // 返回函数 funTwo

以上代码就构成了一个闭包,其实就是函数 fun。

闭包的用途

在介绍闭包的作用之前,我们先来了解一下 JavaScript 中的 GC(垃圾回收)机制。

在 JavaScript 中,如果一个对象不再被引用,那么这个对象就会被 GC 回收,否则这个对象会一直保存在内存中。在上面的例子中,内部函数 funTwo() 定义在外部函数 funOne() 中,因此 funTwo() 依赖于 funOne(),而全局变量 fun 又引用了 funTwo(),所以 funOne() 间接的被 fun 引用。因此 funOne() 不会被 GC 回收,会一直保存在内存中,如下例所示:

function funOne(){
    var num = 0;
    function funTwo(){
        num++;
        console.log(num);
    }
    return funTwo;
}
var fun = funOne();
fun();      // 输出:1
fun();      // 输出:2
fun();      // 输出:3
fun();      // 输出:4

num 是外部函数 funOne() 中的一个变量,它的值在内部函数 funTwo() 中被修改,函数 funTwo() 每执行一次就会将 num 加 1。根据闭包的特点,函数 funOne() 中的变量 num 会一直保存在内存中。

当我们需要在函数中定义一些变量,并且希望这些变量能够一直保存在内存中,同时不影响函数外的全局变量时,就可以使用闭包。

闭包的高级用法

上面介绍的是闭包最原始的写法,在实际开发中,通常会将闭包与匿名函数结合使用,如下例所示:

var funOne = (function(){
    var num = 0;
    return function(){
        num++;
        return num;
    }
})();
console.log(funOne());      // 输出:1
console.log(funOne());      // 输出:2
console.log(funOne());      // 输出:3

此外,同一个闭包机制可以创建多个闭包函数出来,它们彼此没有联系,都是独立的,如下例所示:

function funOne(i){
    function funTwo(){
        console.log('数字:' + i);
    }
    return funTwo;
};
var fa = funOne(110);
var fb = funOne(111);
var fc = funOne(112);
fa();       // 输出:数字:110
fb();       // 输出:数字:111
fc();       // 输出:数字:112

闭包的缺点

内存占用:闭包会占用额外的内存空间。当函数形成闭包时,它会包含自身的代码和相关的环境变量,这些变量会一直存在于内存中,直到闭包不再被使用。如果闭包函数很复杂或存在大量的变量,它可能会占用较多的内存空间。

循环引用:闭包中可能存在循环引用的问题。当闭包函数引用了外部函数的变量,而这些变量又引用了闭包函数,就形成了循环引用。在这种情况下,即使外部函数执行完毕后,由于闭包仍然引用了外部函数的变量,导致无法释放这部分内存,增加了垃圾回收的复杂性。

难以理解和调试:闭包可以在不同的作用域中共享变量,这增加了代码的复杂性,使得代码难以理解和调试。当闭包嵌套层级较多时,对于变量的来源和状态的跟踪变得困难,容易引发错误。

性能损耗:由于闭包中包含了自由变量,访问这些变量需要额外的查找过程,可能会导致性能损耗。与直接访问局部变量相比,闭包中的变量访问可能较慢。

潜在的安全问题:如果闭包函数中的自由变量是可变对象,并且被闭包函数修改,这可能导致意外的结果。在多线程环境下,如果多个线程同时访问和修改闭包中的共享变量,可能引发竞态条件和其他线程安全问题。

尽管闭包存在一些缺点,但在某些情况下它们仍然是有价值的工具。对于简单的程序或小规模的项目,这些缺点可能不会带来太大的影响。然而,在大型项目中,特别是在对性能和内存占用要求较高的场景下,需要谨慎使用闭包,并确保对其进行适当的管理和优化。

猜你喜欢

转载自blog.csdn.net/Itmastergo/article/details/132099284