Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 75 additions & 14 deletions 1-Scopes.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,83 @@ function aDeed () {
return "Taps";
}

var hero = aHero();
var newSaga = function() { // <-- scope1 starts here
var foil = aFoil();
var saga = function() { // <-- scope2 starts here
var deed = aDeed();
console.log(hero+deed+foil);
}; // <-- scope2 ends here
var hero = aHero(); /* 1. before this first line of code even runs, the interpreter will start out by setting up
an execution environment. Step 1 to create an in-memory global scope context to hold all
global variables (building up a storage system for the global scope).At this point(before
any code executes), the global context is the only context that exists, so of course
it is the current execution context(the one in which the interpreter starts its variable lookups in.)
As the first line of code runs, the interpreter builds up a new key-value mapping inside of the
execution context. this in order to keep track of the value that is bound to the name, hero.
Let's say the aHero function randomly generated the value 'Gal', which will be stored in the
hero variable so that
hero = 'Gal' */

var newSaga = function() { /* 2. Like the line above it, this line also implies a simple
asignment operation. Now, the value being assigned is a function that spans several
lines. For now the interpreter will ignore the body of the function(it will run when
the function is actually called).
This line of code has the effect of adding a new key-value pair to our global scope context.
newSaga = {f} */

var foil = aFoil(); /* 4. This line will add a new variable to the current scope
foil = "Cow" */
var saga = function() { /* 5. This line adds another variable to the now
current scope, this time holding a function value.
saga = {f} */
var deed = aDeed(); /* 7. Running the saga function will build yet a new execution context
and move the interpreter's lookup focus into it. This line adds a new variable
to this local scope
deed = "Taps" */
log(hero+deed+foil); /* 8. Then we do lookups on all 3 of our variables. Again, the interpreter
will do this: by scanning outwards from its current context, looking for
the closest containing context that can satisfy a requested name.
First it looks for 'hero', which cannot be found in the current context,
or the next,and so falls through all the way to the global context, where
the variable is found. The string being logged is starting to take shape.
Next, it goes looking for the 'deed' variable. This one turns out to be
easier for the interpreter as it finds it locally without needing to consider
any of the fall-through scopes.So far ("GalEyes..). Lastly, the interpreter
does a lookup for foil, which falls out of the local scope but is found
in the middle scope. Now the interpreter is done with lookups and it sends
the full concatenated string as output to
the log in system("GalTapsCow")
This was also the last line of the function, so the interpreter can jump
back out to where that function was called(6) and resume processing
from there.
Along with it the focal context will shift out to where it was then.*/
};
saga(); /* 6. At this point we will have to do a lookup on the saga variable, in
order to know which function these parens are attempting to call. The interpreter
handles this by checking whether the name saga has any meaning in the current
execution context. the interpreter checks and finds that the saga variable is
available locally in that scope. The value found there is a function object, and
the parens next to the word saga tell the interpreter to invoke that function.*/
saga();
// -> GalTapsCow
saga();
// -> GalTapsCow
}; // <-- scope1 ends here
newSaga();
// -> GalTapsCow

};
newSaga(); /* 3. Moving to running the newSaga(); function should have the effect
of creating a new executioncontext, so that it can make room for new variables that are
local to that function.Now, this context will become the new current context
for as long as that function is running */

newSaga();
// -> GalTapsCow


/* Difference between lexical scope and in-memory scope?

This starts to become clear when saga(); runs for the second time. the process we just went through will repeat,
but running saga a 2nd time will create a totally new execution context, instead of referencing the original
most inner context.This accomodates for storage of different bindings. As ever, the interpreter lookup focus
will also shift into the newly created context. The var declaration creates a new variable in this context
deep = "Tips".
Given the current in-memory conditions, it will log: "GalTipsCow" (The variable lookups start from the current
context, the inner most, and scan out from there to any parent context)

With the inner function complete, the interpreter jumps back out to where that function was called and the current
context moves out as well. Furthermore this was all part of the fist call to newSaga, which is now completed.
Running newSaga function a second time (middle scope), will result in drafting a new, separate middle scope,
itself containing 2 two most inner scopes.
Almost all the exact same steps would occur, but the values would be different and it is stored in a new variable.
The 2 function objects were created by the same lines of code, but in 2 different invocations of the newSaga function.


83 changes: 83 additions & 0 deletions 2-Closures.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,85 @@
// HOW TO USE THESE NOTES:
// Use these notes to follow along with the lesson. You can run the code in this file to reproduce what you see in the videos.

/* Closures: Put simply every function should have access to all the variables from all the scopes that
sorround it. A closure is any function that somehow remains available after those outer scopes have returned.

This is the saga code from earlier.*/

// var hero = aHero();
// var newSaga = function(){
// var foil = aFoil();
// var saga = function(){
// var deed = aDeed();
// log(hero+deed+foil);
// };
// saga();
// saga();
// };
// newSaga();
// newSaga();

// Lets refactor it, and run a new version on our simulated interpreter.

var sagas = []; /* 1. lets make a global variable called sagas that will store an array for us,
and we'll use this array to eventually store saga functions. let's try running this
code with our simulated interpreter again.
// var hero = aHero(); 2. then, the line that assigns a new value to the hero variable will run and
create some random string, in this case 'Boy', to string in the hero variable. */
// var newSaga = function(){ 3. yet another assignment, so a function object gets placed into the newSaga variable.
// var foil = aFoil(); 5. Invoquing any function creates a new context, and that context is where
// all the lookups will start. We add the variable foil to this context. foil is
// randomly selected to be 'Rat'
sagas.push(function(){ /*Then all we would need in order to have permanent access to each of these
saga functions is topush them into that global array. At point we could choose
to access these functions even outside the scope they were defined within.
The invocations of those saga functions will actually happen after the
newSaga function that created them was done running
6. Here is the interesting
part: the sagas array is now stored in a global variable, which means,
calls to 'push' should have a lasting effect on it that persists even
after newSaga has finished running. The function(){...} syntax returns
a function object, which is pushed to the end of the sagas array(from the f to
the end curly brace is the definition of a function object, and that is the
value that we're pushing into the sagas array)*/
// var deed = aDeed(); 9. deed = 'Eyes'
// log(hero+deed+foil); 10. we log a run on all 3 variables: "BoyEyesRat"
// }); 7. When this line of code runs, that means that a new function has been
// added to the sagas array, and even though that {f} is being referenced by
// a variable in the global scope, the function itself has the innermost scope
// access because is fundamentally an innermost function that originated inside
// the newSaga function.
// };
// newSaga(); 4. and we are ready to do a lookup of newSaga, find this function object, and
// invoque it because of those 2 parens.
sagas[0](); /*8. So we have reached the big question: what do you think is going to happen
when you try to run one of these innermost functions that was created in the
middle context but is now being accessed from the global context?
Hint: the context for a function will always be created as a child of the
context that it was defined within.
*/
sagas[0](); //11. completed the first sagas call, we move again to the global context and
// run it again, which will mean a 2nd, totally separate innermost context
// within newSaga
// newSaga(); 12. Here we are about to run the newSaga function for a 2nd time, and the
// important issue here is that it will create a brand new middle context, will
// will be the home for a new 'foil' variable, and 2 new innermost contexts.
// Also, as a result of the 2nd call to .push,
//the sagas array is now going to have 2 different function objects in it.
sagas[0](); //13. Running the first inner sagas function again, will create another, 3rd,
// innermost context inside the first newSaga context.
// The first of the saga functions was created in the first of the newSaga context.
sagas[1](); // 14. We are at another interesting juncture. about to run
// the second sagas function for the first time. What will happen? As usual
// it will create a new context for the function invocation but this time it
// will exist within the second of the middle contexts.
sagas[0]();
/*
what if we could somehow keep a reference to each of the saga functions that we create during the invocation
of newSaga? and we could keep them around forever, such that they were available long after those newSaga
calls had completed and returned?
*/

var sagas = []; // global array to store saga function objects, accessible outside of the newSaga function
var hero = aHero();
var newSaga = function(){
Expand All @@ -18,3 +97,7 @@ sagas[0]();
newSaga();
sagas[1]();

/*lAST FEW WORDS ON CLOSURES: Anytime you see a function with an input parameter that is static, meaning, you don't expect the
parameter to take a new value every time you call the function, that's an opportunity to refactor your code,
such that you store the value in a variable from an outer scope. Because of the way Closures work,
the inner function will always have access to the outer scope variable, even after the outer function returns.*/