- Mastering Dart
- Sergey Akopkokhyants
- 801字
- 2021-08-05 17:19:18
Functions and closures in different scopes
I like Dart because everything is an object. Functions are first-class citizens because they support all the operations that are generally available to other types. This means each function have the following properties:
- They can be named by a variable
- They can be passed as an argument to a function
- They can be returned as the result of a function
- They can be stored in data structures
- They can be created in any scope
Let's see where and how can we use functions as usual or as first-class citizens.
Naming functions with a variable
Naming functions by variable means that we can create a reference to a function and assign it to a variable, as shown in the following code:
library function_var; // Returns sum of [a] and [b] add(a, b) { return a + b; } // Operation var operation; void main() { // Assign reference to function [add] operation = add; // Execute operation var result = operation(2, 1); print("Result is ${result}");}"); }
Here is the result of the preceding code:
Result is 3
We have the add
function and the operation
variable. We assign the reference of the add
function to a variable and call the variable as a function later.
Passing a function as an argument to another function
Passing functions as arguments to other functions can be very useful in cases when we need to implement the strategy design pattern to enable the program code to be selected and executed at runtime, as shown in the following code:
library function_param; // Returns sum of [a] and [b] add(a, b) { return a + b; } // Operation executor executor(operation, x, y) { return operation(x, y); } void main() { // Execute operation var result = executor(add, 2, 1); print("Result is ${result}"); }
Here is the result of the preceding code:
Result is 3
The global executor
function from the preceding example can call any function that accepts two arguments. You can see the implementation of the strategy design pattern in the form of anonymous functions passed as parameters of methods in collections.
Returning a function as a result of another function
Sometimes, a function can be returned as a result of another function, as shown in the following code:
library function_return; // Returns sum of [a] and [b] add(a, b) => a + b; // Returns difference between [a] and [b] sub(a, b) => a - b; // Choose the function depends on [type] chooser(bool operation) =>operation ? add : sub; void main() { // Choose function depends on operation type var operation = chooser(true); // Execute it var result = operation(2, 1); // Result print("Result is ${result}"); }
Here is the result of the preceding code:
Result is 3
This option can be very useful in implementing closures.
Storing a function in data structures
We can store a function in data structures in any collection, as shown in the following code:
library function_store; // Returns sum of [a] and [b] add(a, b) => a + b; // Returns difference between [a] and [b] sub(a, b) => a - b; // Choose the function depends on [type] var operations = [add, sub]; void main() { // Choose function from list var operation = operations[0]; // Execute it var result = operation(2, 1); // Result print("Result is ${result}"); }
Here is the result of the preceding code:
Result is 3
We have two functions and the array operations in our example that stores references to them.
Closures
A function can be created in the global scope or within the scope of another function. A function that can be referenced with an access to the variables in its lexical scope is called a closure, as shown in the following code:
library function_closure; // Function returns closure function. calculate(base) { // Counter store var count = 1; // Inner function - closure return () => print("Value is ${base + count++}"); } void main() { // The outer function returns inner var f = calculate(2); // Now we call closure f(); f(); }
Here is the result of the preceding code:
Value is 3 Value is 4
We have the calculate
function, which contains the count
variable and returns a an inner function. The inner function has an access to the count
variable because both are defined in the same scope. The count
variable exists only within the scope of calculate
and would normally disappear when the function exits. This does not happen in this case because the inner function returned by calculate
holds a reference to count
. The variable has been closed covered, meaning it's within a closure.
Finally, we know what a first-class function is, where we can use them, and how important it is to use closures. Let's move ahead to classes and mixins.