diff --git a/1-Scopes.js b/1-Scopes.js index 73d82f9..1e25c20 100644 --- a/1-Scopes.js +++ b/1-Scopes.js @@ -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. + diff --git a/2-Closures.js b/2-Closures.js index 2aaf46b..d1b1ec6 100644 --- a/2-Closures.js +++ b/2-Closures.js @@ -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(){ @@ -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.*/