- 3.1 Declaring Functions
- 3.2 Higher-Order Functions
- 3.3 Function Literals
- 3.4 Arrow Functions
- 3.5 Functional Array Processing
- 3.6 Closures
- 3.7 Hard Objects
- 3.8 Strict Mode
- 3.9 Testing Argument Types
- 3.10 Supplying More or Fewer Arguments
- 3.11 Default Arguments
- 3.12 Rest Parameters and the Spread Operator
- 3.13 Simulating Named Arguments with Destructuring
- 3.14 Hoisting
- 3.15 Throwing Exceptions
- 3.16 Catching Exceptions
- 3.17 The finally Clause
- Exercises
3.6 Closures
ThesetTimeoutfunction takes two arguments: a function to execute later, when a timeout has elapsed, and the duration of the timeout in milliseconds. For example, this call says “Goodbye” in ten seconds:
setTimeout(() => console.log('Goodbye'), 10000)
Let’s make this more flexible:
const sayLater = (text, when) => { let task = () => console.log(text) setTimeout(task, when) }
Now we can call:
sayLater('Hello', 1000) sayLater('Goodbye', 10000)
Look at the variabletextinside the arrow function() => console.log(text). If you think about it, something nonobvious is going on. The code of the arrow function runs long after the call tosayLaterhas returned. How does thetextvariable stay around? And how can it be first'Hello'and then'Goodbye'?
To understand what is happening, we need to refine our understanding of a function. A function has three ingredients:
A block of code
Parameters
The free variables—that is, the variables that are used in the code but are not declared as parameters or local variables
A function with free variables is called aclosure.
In our example,textis a free variable of the arrow function. The data structure representing the closure stores a reference to the variable when the function is created. We say that the variable iscaptured. That way, its value is available when the function is later called.
In fact, the arrow function() => console.log(text)also captures a second variable, namelyconsole.
But how doestextget to have two different values? Let’s do this in slow motion. The first call tosayLatercreates a closure that captures thetextparameter variable holding the value'Hello'. When thesayLater方法退出,因为该变量不消失se it is still used by the closure. WhensayLateris called again, a second closure is created that captures a differenttextparameter variable, this time holding'Goodbye'.
In JavaScript, a captured variable is a reference to another variable, not its current value. If you change the contents of the captured variable, the change is visible in the closure. Consider this case:
let text = 'Goodbye' setTimeout(() => console.log(text), 10000) text = 'Hello'
In ten seconds, the string'Hello'is printed, even thoughtextcontained'Goodbye'when the closure was created.
The fundamental idea of a closure is very simple: A free variable inside a function means exactly what it means outside. However, the consequences are profound. It is very useful to capture variables and have them accessible indefinitely. The next section provides a dramatic illustration, by implementing objects and methods entirely with closures.