eclipse关闭代码说明
(本文是来自旧金山的软件工程师Nathan Thomas正在进行的有关软技能和技术向导的系列文章的一部分。单击 此处 ,以获得系列文章中的上一篇文章“ Building Your First GraphQL Server”。单击 此处 以获取下一篇文章系列文章中的一篇文章,名为“构建您的第一个GraphQL服务器”。)
从外而内
解释闭包是一个经常使用JavaScript访谈问题,但是许多工程师仍然不完全了解它们的工作方式。 周围弥漫着雾气,就像假期中吃得太多时的感觉。 🥧
当我刚开始学习关闭方法时,我的第一要冲动就是关闭笔记本电脑的屏幕,然后呆呆地盯着当地的池塘。 但是不用担心; 闭包吸引了很多人,但是到本文结尾,您将了解它们的工作原理和背后的概念。
我将向您介绍一个非常酷(更重要的是, 简单 )的示例。 您将很快获得它。
拿起您喜欢的咖啡,苹果酒,热巧克力或冬季饮料,让我们开始吧。 ☕️
“我会以任何借口购买新的背包。” -迈克尔·波茨
拿起你的背包
封闭是一个有趣的概念。 它们在您的代码中是真正不可见的,但它们是非常真实的。 如果您编写了任何JavaScript,几乎可以肯定地使用了它们,甚至没有意识到。
如果没有别的,那应该给你安慰; 如果您已经意外地成功使用了它们,那么成功的一半将是学习它们背后的想法,以填补它们工作方式的空白。
从根本上讲,闭包是变量的无形分隔,可在各个级别(例如,全局范围,局部函数范围等)访问您的代码。
每次编写函数时,都会为其可用的作用域创建一个闭包。 JavaScript会自动执行此操作,因此我们需要做的就是了解它。
例如,假设我们有以下代码:
const globalVariable = 1 ;
function createClosure () {
const localVariable = 2 ;
return function () {
return globalVariable + localVariable;
}
}
const closure = createClosure();
console .log(closure()); // returns 3
此代码会将数字3
到控制台。 JavaScript为幕后代码创建了多个作用域; 有一个与全局范围globalVariable
中,并与当地的范围localVariable
和 globalVariable
在他们访问。
这是包含注释的相同代码,以指示哪些部分是全局范围和局部范围:
// global scope with globalVariable available
const globalVariable = 1 ;
function createClosure () {
// local scope with localVariable and globalVariable available
const localVariable = 2 ;
return function () {
// local scope with localVariable and globalVariable available
return globalVariable + localVariable;
}
}
const closure = createClosure();
console .log(closure()); // returns 3
停下来想一想这里发生了什么。 createClosure函数可以访问两个变量。
如果我告诉过您,当函数在其他地方(甚至是另一个文件)使用时,可以在函数创建时使用闭包访问作用域中所有变量的功能该怎么办?
我知道。 科学走得太远了吗?
让我们找出答案。 🧪
“ 一个好的旅行者没有固定的计划,也不打算到达。”-老子
闭包计数
这是本文其余部分将要处理的代码。 继续阅读。 👍
如果您还不了解它与闭包之间的关系,请不要担心。 只需了解一下代码实际上在做什么:
function createCountingClosure ( startingNum ) {
if ( typeof startingNum !== "number" ) return null ;
let num = startingNum;
function increment () {
num++;
return num;
}
function decrement () {
num--;
return num;
}
function getNum () {
return num;
}
return [increment, decrement, getNum];
}
当我们调用该函数时,会得到以下结果:
const [increment, decrement, getNum] = createCountingClosure( 0 );
此外,我们可以调用这些increment
, decrement
,并getNum
功能得到如下:
increment();// 1
increment(); // 2
increment(); // 3
decrement(); // 2
increment(); // 3
getNum(); // 3
哇
不用担心 我们将详细研究这段代码,以弄清楚到底发生了什么。
(旁注-我们正在这里进行一些重组。如果您不知道什么是重组,请不用担心。请在此处查看MDN的精彩文章。)
让我们再次看一下createCountingClosure
函数并分解每个部分:
function createCountingClosure ( startingNum ) {
if ( typeof startingNum !== "number" ) return null ;
let num = startingNum;
function increment () {
num++;
return num;
}
function decrement () {
num--;
return num;
}
function getNum () {
return num;
}
return [increment, decrement, getNum];
}
首先,我们接受一个名为startingNum
的参数。 我们显然希望它是一个数字(因为下一行是if语句,如果不是,则返回null
)。
接下来,我们将startingNum
的值分配给num
变量。 这为我们的计数器功能提供了一个开始使用的值,以后可以更改。
然后,我们继续声明三个函数:
-
increment
,增加num
的值并返回其值 -
decrement
,它减小num
的值并返回其值 -
getNum
,它返回num
的当前值
最后,我们将这三个函数返回到数组中。 由于函数是JavaScript中的“一等公民”,因此我们可以像这样传递函数,以从函数中返回并在其他地方使用。
这就是为什么我们以后可以调用createCountingClosure并对这三个函数进行解构的原因:
const [increment, decrement, getNum] = createCountingClosure( 0 );
如您所见,我们还将0
传递给函数。 还记得我们如何设置它以接受startingNum
参数吗? 在此示例中,该值将为0
。
综上所述,我们现在讨论createCountingClosure
函数,在这里我们可以讨论闭包的工作方式。
坚持住你的屁股。
![](https://i-blog.csdnimg.cn/blog_migrate/282984cefc33537ab382af92a360255c.png)
包里面有什么?
让我们再次看一下createCountingClosure
函数:
function createCountingClosure ( startingNum ) {
if ( typeof startingNum !== "number" ) return null ;
let num = startingNum;
function increment () {
num++;
return num;
}
function decrement () {
num--;
return num;
}
function getNum () {
return num;
}
return [increment, decrement, getNum];
}
当我们宣布increment
, decrement
和getNum
,我们在不知不觉中创造了他们每个人的那家商店访问可用的所有变量在其范围时,创建这些功能关闭小“背包”。
还记得本文开始时我们是如何进行全球范围和本地范围的吗? 还记得我们创建的函数如何在本地范围内保留对全局范围内的globalVariable
访问吗?
好吧,同样的事情在这里发生。 对于实例, increment
功能保留对num
变量的访问。 实际上,所有这些函数以后仍然可以在我们可能使用它们的任何位置访问该变量(即使它在另一个文件中)。
例如,还记得我们调用createCountingClosure
时如何对所有这些函数进行结构createCountingClosure
?
const [increment, decrement, getNum] = createCountingClosure( 0 );
这是如上所述的increment
函数在被解构时可以访问(在代码中)的内容:
let num = startingNum;
function increment () {
num++;
return num;
}
即使我们仅对increment
了increment
,它仍然可以访问num
变量。 它位于功能的关闭背包中,就像很小的尤达一样。
这意味着即使我们没有直接在函数内部包含它,也可以访问和修改 num
变量。
那不疯狂吗? 🦁
decrement
和getNum
也是如此。 从创建时开始,它们都可以以相同的方式访问num
。
让我们继续使用我们的代码,看看它是如何工作的。
“如果我能看到这个世界,我会很高兴地背着背包生活。” -未知
更新背包
我们将使用我们的代码并逐步完成它,以观察它的作用。 在开始之前,请先整理一下整个过程:
function createCountingClosure ( startingNum ) {
if ( typeof startingNum !== "number" ) return null ;
let num = startingNum;
function increment () {
num++;
return num;
}
function decrement () {
num--;
return num;
}
function getNum () {
return num;
}
return [increment, decrement, getNum];
}
另外,我们将像以前一样调用createCountingClosures
函数并解构数组中返回的函数:
const [increment, decrement, getNum] = createCountingClosure( 0 );
通过此设置,我们现在可以开始使用了。 如果您想在我们浏览本文其余部分的同时进行操作,这里有一个Codepen链接 ,可让您与代码进行交互。
首先,我们将调用增量函数:
increment();// should return 1
如前所述, increment
函数保留对num变量的访问。 当我们调用increment
,该函数将在后台增加num
的值。
由于我们最初在上方调用createCountingClosures
时传入了0
值,因此我们将该值增加到1
。
(如果您遵循我提供的CodePen,则会在控制台中看到该弹出窗口,因为我已将函数调用包装在console.log
。)
接下来,我们将再调用两次增量,如下所示:
increment();// should return 2
increment(); // should return 3
注意,每次返回的值都不是从1
开始; 这是因为我们上面使用的相同变量num
也被increment
保留,并在每次调用时进行修改。 这是关闭的不可思议的力量!
接下来,我们将调用函数decrement
来减小num
的值:
decrement();// should return 2
请注意,该值再次不是从0
开始。 函数decrement
和increment
都保留对同一num
变量的访问,并且共享修改它的能力,即使该变量未包含在两个变量中。
最后,我们getNum
调用一次increment
并通过调用getNum
函数(此函数仅返回num
的当前整数值)来限制increment
:
increment();// should return 3
getNum(); // 3
所有这些函数调用都可以访问num变量,因为它们所有对它的引用都存储在它们的小范围“背包”中。
最终的getNum
调用返回3,这是我们对其进行的所有更改中的num
的值。 即使num
并不直接包含在我们一直调用的任何函数中,我们还是通过闭包的功能对其进行了修改。
如果您仍然难以掌握封包的工作原理,以下是MPJ(在YouTube上运行Fun Fun Function频道)上的精彩短片,介绍了封包的工作原理:
结论
我希望这个关于闭包如何工作的简短介绍可以帮助您掌握它们。
通过将它们想象成函数在应用程序中移动时会磨损的小背包,您可以在心中建立一个框架,了解如何访问和操作闭包中包含的变量。
另外,既然我们已经逐步介绍了它们的工作原理,那么您可以对自己的结业知识充满信心,并粉碎下一次面试的机会。
谢谢阅读。 🔥
内森
( Twitter的 , LinkedIn , GitHub 和 Portfolio网站 )
翻译自: https://hackernoon.com/please-explain-closures-g73q3w1h
eclipse关闭代码说明