by William Countiss
威廉·Countiss
结束书 (Closing the Book on Closures)
JavaScript closures are an important, but notoriously confusing concept. There’s no escaping it — if you want to grow as a developer, you need to understand what closures are and how to use them.
JavaScript闭包是一个重要的概念,但众所周知令人困惑。 没有逃避之路-如果您想成长为一名开发人员,则需要了解什么是闭包以及如何使用它们。
Don’t let the fancy name scare you — once you play around with closures a bit you’ll realize that there really isn’t much to them.
不要让这个奇特的名字吓到您-一旦您对闭包进行一点操作,您就会意识到它们确实没什么用。
Let’s start with something simple:
让我们从简单的事情开始:
1 function sayGreeting(greeting) { 2 3 return function(name) { 4 5 console.log(greeting + " " + name); 6 } 7 8 }
You’ll notice right away that our function, sayGreeting, returns another function. I can do this in JavaScript because functions are considered first-class, which means that they can be passed around just like other data types such as a number, string, or boolean. This can make for some interesting syntax:
您会立即注意到我们的函数sayGreeting返回了另一个函数。 我可以在JavaScript中进行此操作,因为函数被认为是一流的,这意味着它们可以像数字,字符串或布尔值之类的其他数据类型一样传递。 这可以产生一些有趣的语法:
1 function sayGreeting (greeting) { 2 3 return function (name) { 4 5 console.log (greeting + " " + name); 6 } 7 8 } 9 sayGreeting("Hello")("William");
So what would you expect to see in the console when we run this code? Think about it for a moment and then take a look at the image below.
因此,当我们运行此代码时,您希望在控制台中看到什么? 考虑一下,然后看下面的图片。
If you guessed “Hello William”, you’re right. Go ahead and give yourself a pat on the back. Now, let’s take a closer look into why.
如果您猜到“ Hello William”,那是对的。 继续拍一下自己的背。 现在,让我们仔细研究一下原因。
sayGreeting("Hello")("William");
Remember that sayGreeting returns a function. As we mentioned earlier, functions in JavaScript are first-class, and may be passed around like any other data structure. So when sayGreeting(“Hello”) is invoked for the first time it executes and returns an anonymous function. A returned function may also be invoked, and that is why you are seeing the second set of parentheses: sayGreeting(“Hello”)(“William”)
请记住,sayGreeting 返回一个函数。 如前所述,JavaScript中的函数是一流的,并且可以像其他任何数据结构一样传递。 因此,当首次调用sayGreeting(“ Hello”)时,它将执行并返回一个匿名函数。 还可能调用返回的函数,这就是为什么您看到第二组括号的原因:sayGreeting(“ Hello”) (“ William”)
To make this a bit easier to follow let’s change the code a little by setting the first invocation to a variable:
为了使它更容易理解,我们将第一次调用设置为变量,以对代码进行一些更改:
1 function sayGreeting (greeting) { 2 3 return function(name) { 4 5 console.log(greeting + " " + name); 6 } 7 8 } 9 10 var sayHello = sayGreeting("Hello"); 11 sayHello("William");
If you run this in your console you’ll get the same result as before. But how does sayHello(“William”) know about the value of the parameter greeting from the sayGreeting function? To understand this, we’ll need to go a little deeper.
如果在控制台中运行此命令,则将获得与以前相同的结果。 但是sayHello(“ William”)如何从sayGreeting函数知道有关参数greeting的值? 要理解这一点,我们需要更深入一些。
Whenever a function is invoked, memory is set aside for that function and its contents, which stick around even after the function has finished executing. We can visualize this by wrapping the sayHello variable with a console.dir()
每当调用一个函数时,都会为该函数及其内容留出内存,即使在函数执行完毕后,这些内存仍会保留。 我们可以通过用console.dir()包装sayHello变量来可视化它
1 function sayGreeting(greeting) { 2 3 return function(name) { 4 5 console.log(greeting + " " + name); 6 } 7 8 } 9 10 var sayHello = sayGreeting("Hello"); 11 12 console.dir(sayHello); 13 sayHello("William");
You’ll see in the console that the variable sayHello is an anonymous function, and within its scope there is a Closure with a name:value pair,
您会在控制台中看到变量sayHello是一个匿名函数,并且在其范围内有一个带name:value对的C 闭环 ,
greeting: “Hello”
问候:“你好”
This should look familiar since “greeting” is the name of the parameter of the sayGreeting(greeting) { … } function on line 1, and “Hello” was the string that we passed into it when we first invoked the function on line 10. Memory was then set aside for these values and is available as an outer reference when we invoke the function on line 13.
这应该看起来很熟悉,因为“ greeting”是第1行的sayGreeting(greeting){…}函数的参数名称,而“ Hello”是我们在第10行首次调用该函数时传递给它的字符串。然后为这些值预留了内存,当我们在第13行调用该函数时,它可用作外部引用 。
To help visualize this let’s write out the body of the sayHello function as it is executed on line 13.
为了使这一过程可视化,让我们在第13行执行时写出sayHello函数的主体。
1 function (name) { 2 3 console.log (greeting + " " + name); 4 }
The string “William” is passed in for the name parameter, then on line 3 console.log(greeting + “ “+ name) is executed.
为name参数传入字符串“ William”,然后在第3行console.log( greeting +““ + name )”被执行。
It then looks for the values of greeting and name.
然后,它查找greeting和name的值。
Our function finds a value for name: “William”. But it doesn’t have a value for greeting. So now it’s time to go fishing, and it looks to its outer reference (where it sits in terms of lexical scope) in an attempt to find a value for greeting.
我们的函数查找名称的值:“ William”。 但这没有问候的价值。 因此,现在该钓鱼了,它查找其外部参考(在词汇范围方面),以期寻找问候的价值。
In other words, it remembers where it was explicitly written in the code, which is inside of the sayGreeting function.
换句话说,它会记住它在sayGreeting函数内部的代码中显式编写的位置。
1 function sayGreeting(greeting) { 2 3 return function(name) { 4 5 console.log(greeting + ' ' + name); 6 } 7 8 }
When it finds the value of greeting in its outer reference, we refer to this as closing in on an outer variable, and when this happens you have closure.
当它在其外部引用中找到greeting的值时,我们将其称为在外部变量上封闭 ,并且当发生这种情况时,您将具有closures 。
That wasn’t so bad, was it?
那还不错,不是吗?
This is a very basic example, but even in complex applications, the rules remain the same. Whenever a function can’t find the value of something within itself, it will follow the scope chain all the way down (or up depending upon how you envision it) and search for that value to create the closure.
这是一个非常基本的示例,但是即使在复杂的应用程序中,规则也保持不变。 每当函数无法在其自身中找到某个值时,它将一直沿作用域链向下(或向上延伸,具体取决于您对它的设想),并搜索该值以创建闭包。
翻译自: https://www.freecodecamp.org/news/closing-the-book-on-closures-50b095289bfa/