Javascript基础-理解闭包

87 阅读3分钟

对于刚接触或者使用一段javascript的开发人员来说,闭包肯定是个难以理解的概念,到底什么是闭包?我为什么要使用闭包?我的代码里面哪些地方出现过闭包?这篇文章将会带着这些问题帮助大家理解这些问题。

1. 闭包的定义

闭包,是一种编程概念,存在于一部分开发语言中如:Javascript、Python、Ruby等。闭包,是基于词法作用域书写代码时所产生的自然结果。

正常情况下,一个函数执行完毕后,其内部创建的局部变量就会被销毁。而闭包,则是一种特性,当一个函数在其定义环境之外的地方被调用而仍然能够访问其定义环境中的变量,就形成了闭包。这样就使得定义环境处的变量在外层函数执行完毕后不能被销毁。

看一段代码:

function outerFunction() { 
    var outerVariable = 'I am from outer function!'; 
    function innerFunction() { 
        console.log(outerVariable); 
    } 
    return innerFunction; 
} 
var myInnerFunction = outerFunction(); 

myInnerFunction(); // 输出:I am from outer function!

在上面的例子中,outerFunction是一个外部函数,它内部有一个局部变量outerVariable和一个内部函数innerFunctioninnerFunction 可以访问 outerFunction 的作用域,也就是 outerVariable,而outerFunction的返回值是innerFunction,这样当outerFunction执行完毕,myInnerFunction将保持对innerFunction的引用,执行myInnerFunction,这样就形成了innerFunction在定义环境之外被执行然而能够访问并使用变脸outerVariable。这就是闭包。

在看下面一个例子:

function foo() { 
    var a = 2; 
    function baz() { 
        console.log( a ); // 2 
    } 
    bar( baz ); 
} 
function bar(fn) { 
    fn(); // 这就是闭包! 
}

上面也是一个闭包的例子,函数baz被当作一个参数传递,脱离了定义环境函数foo内部执行,转而在函数bar内部执行,但是函数baz依然能够访问定义环境foo内部的变量a。这就是闭包。

也可以说,无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时都可以观察到 闭包。

再看下面的一个函数结构,我们大多数人肯定都写过这样的结构:

function wait(message){
    setTimeout(function timer(){
        console.log(message)
    }, 1000)
}

wait('hello wait 1000')

上面是一个等待执行函数,将一个内部命名函数传递给了setTimeout。wait函数执行完毕,而内置的工具函数 setTimeout(..)依然 持有对一个参数的引用。

2.总结

看过了前面的例子,我们可以很好的理解什么是闭包,当一个函数在定义的环境之外被执行而依然访问定义时所在的环境里面的变量时候,变形成了闭包。闭包使得外层函数执行完毕内部创建的变量不会被垃圾回收。

虽然闭包在一些场景中有很多用处,如隐藏内部实现、封装模块等。但是它也可能带来一些问题,如:

  • 内存消耗:使用闭包会占用更多的内存空间,因为它需要保存外部环境中的变量和函数。如果你的程序大量使用闭包,可能会导致内存消耗过大,甚至出现内存泄漏的情况。
  • 代码可读性:闭包可能会导致代码的可读性降低,因为它增加了代码的复杂性,需要更多的理解和分析。
  • 命名冲突:在闭包中,如果局部变量与全局变量名称相同,那么在闭包内部访问该变量时,可能会不小心引用到全局变量,从而导致意料之外的结果。
  • 意外改变:在闭包外部,闭包可以改变父函数内部变量的值。如果父函数当作对象使用,闭包当作它的方法使用,那么要小心不要随便改变父函数内部变量的值。