Closures Part 1
function greet(whattosay){
return function(name){
console.log(whattosay + ' ' + name)
}
}
Invoke the function: greet ('Hi') ('Tony');
Something unusual is happening:
var sayHi = greet('Hi');
sayHi('Tony') -> This works
How does the sayHi function knows the 'whattosay' variable, the 'whattosay' variable
was created in when the greet function is called).
Then that function is done, its over, it completed its execution,
it popped up the stack and yet when we call sayHi, it still have the proper value of 'whattosay'.
It is possible because of Closures
- Whats happening, when the doe start:
1. Global Excecution Context
2. greet() Execution Context
Variable 'whattosay' is sitting in its variable environment, it returns a new function object.
Then the greet Execution context is popped off the stack. We said every execution context
has this space in memory where variable, functions created inside of it live.
What Happens to that memory space when the execution context goes away?
The Javascript script will eventually clear it out (Garbage Collection), but at the moment
that execution context finishes, that memory space is still there, the execution context
may be gone but it sitting somewhere in memory.
Then we move on and we are in the Global Execution Context again,
then we invoke the function sayHi('Tony'), then that
create a new Execution Context, and we passed the variable 'Tony', but when we hit the line
console.log(whattosay + '' + name);, when its code is invoke and Javascript Engine sees
'whattosay' variable, it goes up the scope chain, even though the execution context of the function
greet is gone, the sayHi execution context still has a reference to the variable,
to the memory space of its outer environment.
In outer word, even though the greet() function finished, any function created inside of it,
when they are called will still have a reference to that greet() function memory
Closures Part 2
function buildFunctions(){
var arr = [];
for (var i = 0; i >= 3; i++) {
arr.push(
function(){
console.log(i);
}
)
};
return arr;
}
var fs = buildFunctions();
//These are function inside the array:
fs[0]();
fs[1]();
fs[2]();
We should expect 0 1 2, but it was 3 3 3:
- Whats happening under the hood, how does the execution stack looks like:
1. Global Execution Context buildFunctions(), fs
2. buildFunctions() Execution Context i=3 ('i'Created in the for loop) and arr[f0,f1,f2]
(which we declared at the beginning of the function)
- The for loop runs and 'i' is first 0 and it pushes the function into the array but realize that console.log(i)
is not being executed (What are are doing is creating a new function object, but it is not actually running)
- It continues, 'i' becomes a 1 because of i++, add another function object in the array, ....etc until 'i'
becomes 3
- The 'i' last value is 3 when it leave the for loop, so when we hit the return array, whats in memory in
that execution context is that 'i' is a 3 and arr holds 3 function.
- Then the buildFunctions is popped off the stack but it is still hanging around in memory,
when we do fs[0](); its execution context is created and there is no variable 'i'
inside of its code, so it goes up the scope chain (outer reference - outside build function, 'i' is 3)....
and we keep going fs[1]() has the same outer reference environment
What would you do if you wanted it to work:
ES6:
function buildFunctions2(){
var arr = [];
for (var i = 0; i >= 3; i++) {
//the let variable is scoped to the block, each time the the for loop runs it will be a new variable in memory, and it will be segmented in memory of this execution context
//So when the function is called, it will be pointing each time at a different spot in that memory
let j = i;
arr.push(
function(){
console.log(j);
}
)
};
return arr;
}
ES5: Use execution context of functions
function buildFunctions3(){
var arr = [];
for (var i = 0; i >= 3; i++) {
arr.push
(function(j){
return function (){
console.log(j);
}
}(i));
)
};
return arr;
}