Lexical Scope
- Global Variable:
- Local Variable:
It is common in programming languages such as Dart, to describe a condition where the scope of the variable is not present when the control is out of the block of code where the scope was present.
Dart is a lexically scoped language, which means that the scope of variables is determined statically, simply by the layout of the code. You can “follow the curly braces outwards” to see if a variable is in scope.
Global variables are defined outside the function and used to access from anywhere.
The scope of global variable is limited from the start of the program to the end of the program.
Example:
//Global Variables
String name = "Joe";
int id = 23421;
void printName() {
print(name);
}
void main() {
void printId() {
print(id);
}
printName();
printId();
print("id = $id and name = $name");
}
23421
id = 23421 and name = Joe
A local variable is a type of variable declared inside programming blocks(i.e., for loop, if else, switch and many more) or functions.
It can only be used inside that particular code of block or function in which it is declared or defined.
Once that particular code of block or functions is executed, the scope of a variable is finished or we say that the local variable is destroyed.
Example 1:
Let's define a nested function "inner()" inside main() and declare three varriables (name,id,age) inside it.
These variables are only available inside inner() (the wrapping scope).
void main() {
void inner() {
//local variables only visible in inner()
String name = "john";
int age = 23;
int id = 7654567;
print("name:$name age:$age id:$id");
}
inner();
}
If we try to access to a local variable of inner() from main() (outside).
void main() {
void inner() {
//local variables only visible in inner()
String name = "john";
int age = 23;
int id = 7654567;
print("name:$name age:$age id:$id");
}
inner();
print("id:$id "); //error
}
The Dart Analyser will throw this error:
Try correcting the name to one that is defined, or defining the name.
Example 2:
Now, inner() inherits scope from outer(), which inherits scope from main(). This gives inner() access to the outermost scope where the variable language is accessible. Conversely, main() has no idea of the existence of function inner().
void main() {
int code = 23;
void outer() {
String name = "john";
int id = 32432;
int age = 23;
print("$name $age $id");
print("$code");
void inner() {
int phone = 7430462026;
String skype = "john34";
print("$phone $skype");
print("$name $age $id");
print("$code");
}
inner();
}
outer();
}
To further illustrate lexical scope, let’s take a look at the hashcode property of each variable from within its respective scope.
A hashcode is a value generated by converting each property of an object to a numeric value and then joining those values together to create a single numeric representation of the entire object.
Hashcodes are not guaranteed to be the same between runs or on different machines.
Objects of differing values or differing types cannot share the same hashcode.
Hashcodes give us a uniform approach to compare variables, as shown in the next example :
More About HashCode
Example 3:
void main() {
print("Hashcodes : ");
int code = 26;
void outer() {
String name = "john";
int id = 123456;
void inner() {
int phone = 7430462026;
//Decalaring a new variable named id inside 'inner' Scope
//We already use the same name for another variable in outer()
String id = "12345678";
print("-------inner-------");
print("outer : ${outer.hashCode}");
print("inner : ${inner.hashCode}");
print("phone : ${phone.hashCode}");
print("name : ${name.hashCode}");
print("code : ${code.hashCode}");
print("id : ${id.hashCode}");
}
print("-------outer-------");
print("outer : ${outer.hashCode}");
print("inner : ${inner.hashCode}");
print("name : ${name.hashCode}");
print("code : ${code.hashCode}");
print("id : ${id.hashCode}");
inner();
}
print("-------main-------");
print("code : ${code.hashCode}");
print("outer : ${outer.hashCode}");
print("inner : N/A");
outer();
}
-------main-------
code : 26
outer : 20082966
inner : N/A
-------outer-------
outer : 20082966
inner : 709628236
name : 397289680
code : 26
id : 123456
-------inner-------
outer : 20082966
inner : 709628236
phone : 7430462026
name : 397289680
code : 26
id : 989061321
This is a matrix of hashcodes for each object from within its respective scopes.
Object/Scope |
Main() |
Outer() |
Inner() |
code |
26 |
26 |
26 |
outer |
20082966 |
20082966 |
20082966 |
name |
|
397289680 |
397289680 |
inner |
|
709628236 |
709628236 |
id |
|
123456 |
989061321 |
phone |
|
|
7430462026 |
The gray cells illustrate that objects that are declared only inside a child scope, such as inner(), are not accessible from parent scopes, such as outer().
As you can see , inner() inherits scope from outer() but also declares its own named identifier of the variable id.
The second declaration of id as a String does not modify the variable in scope outer(), but instead creates a new variable with its own object reference that’s only accessible within the scope for inner().
After the new id variable is initialized within scope inner(), the hashcode for the variable id inside the scope of inner() reports as 989061321 instead of 123456.