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/