闭包就是一个函数,或者一个指向函数的指针,加上这个函数执行的非局部变量。 说的通俗一点,就是闭包允许一个函数访问声明该函数运行上下文中的变量,甚至可以访问不同运行上文中的变量。 我们用脚本语言来看一下:
function funA(callback){ alert(callback()); }
function funB(){ var str = “Hello World”; // 函数funB的局部变量,函数funA的非局部变量 funA( function(){ return str; } ); } 通过上面的代码我们可以看出,按常规思维来说,变量str是函数funB的局部变量,作用域只在函数funB中,函数funA是无法访问到str的。但是上述代码示例中函数funA中的callback可以访问到str,这是为什么呢,因为闭包性。
您提到的这段JavaScript代码很好地展示了闭包的概念。为了更好地理解闭包性,我们先来梳理一下这段代码的运行过程:
函数定义:
function funA(callback){...}
定义了一个名为funA
的函数,它接受一个参数callback
,该参数是一个函数(即回调函数)。function funB(){...}
定义了一个名为funB
的函数,它内部声明了一个局部变量str
并赋值为"Hello World"
,然后调用funA
并传入一个匿名函数作为参数。
函数调用:
- 调用
funB()
,执行funB
函数体内的代码。
以下是
funB
执行过程中的关键步骤:a. 声明并初始化局部变量
str
为"Hello World"
。b. 调用
funA
,传入一个匿名函数(即回调函数)作为参数。这个匿名函数的定义如下:function() { return str; }
注意,这个匿名函数内部直接引用了
str
变量。c.
funA
接收到这个匿名函数作为参数callback
,并调用它:alert(callback());
。- 调用
现在我们来解释闭包性在这个例子中的作用:
闭包 是一个有权访问另一个函数作用域内变量的函数。当一个函数(内部函数)被定义在一个作用域(通常是另一个函数的作用域)内,并且该内部函数引用了该作用域内的变量,同时这个内部函数被返回或者作为参数传递到外部作用域中使用时,就形成了一个闭包。
在这个例子中,匿名函数(作为 funB
的参数传给 funA
的回调函数)就是一个闭包,它具有以下几个特点:
访问外部作用域变量:匿名函数内部直接引用了
funB
的局部变量str
。虽然按照常规的词法作用域规则,str
应该仅在funB
内部可见,但由于闭包的存在,这个匿名函数“捕获”了str
的引用,使其能够在funA
的作用域内访问到。保持变量存活:通常情况下,当一个函数执行完毕,其内部的局部变量会被销毁。然而,由于
funB
传递给funA
的匿名函数(闭包)仍然保留了对str
的引用,所以即使funB
执行完毕,str
的值也不会被立即销毁。闭包保持了对str
的引用,使得该变量在其生命周期内得以延续。跨作用域访问与更新:当
funA
在其内部调用callback()
时,实际上是执行了这个闭包。闭包此时能够访问到str
的值(“Hello World”),并将其作为返回值。因此,尽管str
是funB
的局部变量,funA
通过闭包仍然能访问到它。
总结来说,这段代码中的闭包性体现在:
- 匿名函数(闭包)在定义时捕获了外部作用域(
funB
)中的变量str
。 - 即使
funB
执行完毕,闭包仍保持对str
的引用,防止其被销毁。 funA
能够通过调用闭包(回调函数)访问到原本在其作用域之外的str
变量。
这就是闭包性如何让一个函数访问到另一个函数运行上下文中的变量,突破了常规的词法作用域限制。闭包是JavaScript(以及其他支持闭包的语言)中非常重要的特性,常用于异步编程、模块封装、数据隐藏等场景。