Dart Closures and Lexical Scoping
Closures and Lexical Scoping in Dart
In Dart, closures are functions that "remember" the environment in which they were created, even if they are executed outside of that environment. This feature is closely tied to lexical scoping, where variables are scoped based on where they are defined in the code, rather than where they are called from.
When a function (closure) is created within another function, it has access to the outer function’s variables even after the outer function has finished executing.
Lexical Scoping
Lexical scoping means that the scope of a variable is determined by its position in the source code. Variables are available in the function or block in which they are declared, and any inner functions (closures) have access to variables from their outer scope.
Here’s an example of lexical scoping:
Output:
In this example, innerFunction
has access to outerVar
, even though outerVar
is defined in the outerFunction
. This is an example of lexical scoping, where innerFunction
can "see" variables from the scope it was defined in.
Closures in Dart
A closure occurs when a function is defined inside another function and captures the variables from the outer function's scope. The inner function "remembers" the values of the variables from its enclosing function, even if the outer function has finished executing. This is the essence of closures.
Let’s see an example of closures in Dart:
Output:
In this example:
makeMultiplier
is a function that returns another function (a closure).- The returned function captures the
multiplier
value from the outer function and uses it when called. multiplyBy2
remembers2
as the multiplier, andmultiplyBy3
remembers3
as the multiplier.
Even though the makeMultiplier
function finishes execution, the closures (multiplyBy2
and multiplyBy3
) continue to have access to the multiplier
variable because they "remember" it. This behavior is due to closures and lexical scoping.
Practical Use of Closures
Closures are commonly used in situations where you need to pass around a function that retains its context, such as for event handling, timers, and asynchronous programming.
Example with Timer (asynchronous closure):
Output (over 5 seconds):
In this example:
- The closure inside the
Timer.periodic
function keeps updating thecounter
variable every second. - The timer "remembers" the
counter
variable, and its state is updated on each timer tick. This is a closure in action, as the function inside the timer has access to variables from its surrounding scope (counter
in this case).
Summary of Closures and Lexical Scoping in Dart
- Lexical scoping refers to how the scope of a variable is determined by where it is defined in the code (the location of the declaration, not where it is used).
- Closures are functions that can capture and remember variables from their enclosing scope, even if that scope is no longer active.
- Closures allow for functions to "remember" the context in which they were created, which is useful for things like callbacks, event handlers, and maintaining state in asynchronous operations.
Closures and lexical scoping are powerful features that can make your code more flexible and maintainable, allowing for more advanced patterns like function factories and maintaining state across function calls.