JavaScript闭包教程–带有JS闭包示例代码

闭包–许多JavaScript开发人员以前可能都听说过这个词。当我开始使用JavaScript时,经常会遇到关闭。而且我认为它们是JavaScript中最重要和最有趣的概念之一。

你不觉得他们有趣吗?当您不了解某个概念时,通常会发生这种情况–您不会觉得它很有趣。(我不知道这是否发生在您身上,但我就是这种情况)。

因此,在本文中,我将尝试使您感兴趣的闭包。在进入闭包世界之前,让我们首先了解词法作用域。如果您已经知道它,请跳过下一部分。否则,请跳入其中以更好地了解闭包。

词汇范围

您可能在想–我知道本地和全局范围,但是词汇范围到底是什么呢?听到这个词语时,我的反应是一样的。不要担心!让我们仔细看看。就像其他两个作用域一样简单:

从上面的输出中可以看到,内部函数可以访问外部函数的变量。这是词法作用域,其中变量的范围和值由定义/创建的位置(即其在代码中的位置)确定。

我知道最后一点可能会让您感到困惑。因此,让我更深入地介绍。您是否知道词法作用域也称为静态作用域?是的,这是它的别称。

还有动态范围,某些编程语言支持。为什么提到动态范围?因为它可以帮助您更好地理解词法作用域。

让我们看一些例子:

它将给出参考错误。这是因为两个函数无法访问彼此的范围,因为它们是分别定义的。

让我们看另一个例子:

对于动态范围的语言,以上输出将为20。支持词法作用域的语言会给出这个:referenceError: number2 is not defined

因为在动态作用域中,搜索首先在本地函数中进行,然后才进入调用该本地函数的函数中。然后,它在调用该函数的函数中进行搜索,依此类推,直到调用堆栈。

它的名字不言自明–“动态”意味着变化。变量的范围和值可以不同,因为它取决于调用函数的位置。变量的含义可以在运行时更改。

有动态范围的要点吗?如果有,那么只需记住词汇范围是相反的。

在词法作用域中,搜索首先在局部函数中进行,然后进入定义该函数的函数中。然后,它在定义该函数的函数中进行搜索,依此类推。

因此,词汇或静态作用域意味着从定义变量的位置确定变量的范围和值。它没有改变。

让我们再次看一下上面的示例,尝试自己找出输出。只需一转- number2在顶部声明:

输出词汇范围语言为12。这是因为,首先,它查找一个addNumbers函数(最内部的作用域),然后向内搜索,定义了该函数。当它获取number2变量时,意味着输出为12。

您可能想知道为什么我在这里花了很多时间进行词汇范围界定。这是一篇闭包文章,而不是有关词法作用域的文章。但是,如果您不了解词法作用域,那么您将不会理解闭包。当我们查看闭包的定义时,您会得到答案。因此,让我们进入正轨,回到闭包。

什么是闭包?

让我们看一下闭包的定义:当内部函数可以访问其外部函数的变量和参数时,将创建闭包。内部函数可以访问

1.其自己的变量。;2.外部函数的变量和参数。;3.全局变量。

这是闭包或词汇作用域的定义吗?两种定义看起来相同。它们有何不同?好吧,这就是为什么我在上面定义词法作用域的原因。因为闭包与词汇/静态作用域有关。让我们再次看一下它的另一个定义,该定义将告诉您闭包有何不同。

闭包是指某个函数能够访问其词法范围,即使该函数正在其词法范围之外执行。内部函数可以访问其父范围,即使已经执行了父函数也是如此。

困惑?如果您还没有明白这一点,请不要担心。我有一些例子可以帮助您更好地理解。让我们修改词法作用域的第一个示例:

这段代码的区别在于我们要返回内部函数并在以后执行它。在某些编程语言中,局部变量在函数执行期间存在。但是一旦函数执行完毕,这些局部变量将不存在,并且将无法访问。

但是,这里的场景有所不同。执行父函数后,内部函数(返回的函数)仍可以访问父函数的变量。

内部函数在执行父函数时保留其词法范围,因此,稍后内部函数可以访问这些变量。为了更好地理解它,让我们使用dir()控制台的方法查看以下属性的列表callGreetCustomer:

从上图可以看到内部函数customerName在greetCustomer()执行时如何保留其父范围()。后来,customerName在callGreetCustomer()执行时使用。

实施中的闭包示例

每次调用时countValue,count变量的值都会增加1。您是否认为count的值为0?嗯,这是错误的,因为闭包不能与值一起使用。它存储变量的引用。这就是为什么当我们更新值时,它反映在第二个或第三个调用中,依此类推,因为闭包存储了引用。

现在感觉更清晰了吗?让我们看另一个例子:

希望您猜对了答案。作为countValue1和countValue2,既保留自己的词汇范围。他们有独立的词汇环境。您可以在两种情况下使用dir()来检查[[scopes]]值。

让我们看第三个例子。

这有点不同。在其中,我们必须编写一个函数来实现输出:

使用新获得的闭包知识:

现在让我们看一些棘手的例子:

每个存储函数的数组元素都将为您提供9的输出。您猜对了吗?希望如此,但仍然让我告诉您原因。这是由于闭包的行为。

闭包存储引用,而不是值。循环第一次运行时,x的值为0。然后第二次循环x为1,依此类推。因为闭包存储了引用,所以每次循环运行时,它都会更改x的值。最后,x的值为9。因此callInnerFunctions[0](),输出为9。

但是,如果您希望输出0到8怎么办?简单!使用闭包。在查看以下解决方案之前,请思考一下:

在这里,我们为每次迭代创建了单独的作用域。您可以console.dir(arrToStore)用来检查[[scopes]]不同数组元素的x in值。

最后,我希望您现在可以说您发现闭包很有趣。

打开APP阅读更多精彩内容