Last active
September 29, 2018 12:05
-
-
Save Anna-Myzukina/7f550427517e36860bc68420b1bf4ead to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 1 | |
/*Scopes | |
The main type of scope in Javascript is Lexical Scoping. Present in the language | |
from the very beginning, this is the scope created within a function, and the | |
one most developers are familiar with.[1] | |
ES6 recently defined Block Scoping. This scope is created within curly braced | |
blocks.[2] | |
## Initializing Variables | |
The way a variable is initialized determines which scope type it is: | |
### Lexical Scope | |
var is used to denote a variable which is Lexically Scoped to the current | |
function: | |
function someFunc() { | |
var aVariable; | |
} | |
aVariable is lexically scoped within someFunc | |
### Block Scope | |
let & const are used to denote variables which are Block Scoped to the | |
current curly braced block: | |
if (true) { | |
let aVariable; | |
} | |
aVariable is block scoped within the if's curly braces | |
------------------------------------------------------------------------------- | |
# Your Mission | |
In an empty file, create a function foo which contains one variable lexically | |
scoped named bar. | |
## Notes | |
* [1]: There are also 4 other scopes in the language: Global, `with`, `catch`, | |
* and `eval`. These tend not to be used much, so we will ignore them. | |
* [2]: This workshop will concentrate only on Lexical Scoping.*/ | |
function foo() { | |
var bar; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 2 | |
/*Modify your solution from lesson 1 so foo, in addition to lexically scoped variable bar, | |
contains a function zip | |
which itself contains one variable lexically scoped called quux*/ | |
function foo() { | |
var bar; | |
function zip(){ | |
var quux; | |
} | |
} | |
/* | |
The scope chain you created now looks like this: | |
(global) | |
↑ | |
| | |
foo() | |
var bar | |
↑ | |
| | |
zip() | |
var quux | |
By following the arrows, we can see zip() has access to var bar, but not the | |
other way around. | |
Scope Chains | |
## Nesting | |
Scopes can be nested. Both Lexical and Block scopes can contain other scopes: | |
function someFunc() { | |
function inner() { | |
} | |
} | |
inner is a nested lexical scope inside the lexical scope of someFunc | |
------------------------------------------------------------------------------- | |
if (true) { | |
while (false) { | |
} | |
} | |
The while is a nested block scope inside the block scope of if | |
------------------------------------------------------------------------------- | |
function someFunc() { | |
if (true) { | |
} | |
} | |
The if is a nested block scope inside the lexical scope of someFunc | |
------------------------------------------------------------------------------- | |
## Scoped Variable Access | |
All nested scopes follow the same rule: Each nested inner scope has access to | |
outer scope variables, but NOT vice-versa. | |
For example: | |
function someFunc() { | |
var outerVar = 1; | |
function inner() { | |
var innerVar = 2; | |
} | |
} | |
inner has access to both innerVar & outerVar, but someFunc only has | |
access to outerVar | |
## Multiple Nested Scopes | |
Nesting isn't limited to a single inner scope, there can be multiple nested | |
scopes, each of which adhere to the Scoped Variable Access rule above. With | |
one addition: sibling scopes are also restricted from accessing each other's | |
variables. | |
For example: | |
function someFunc() { | |
function inner() { | |
} | |
function inner2() { | |
} | |
} | |
inner & inner2 are both inner scopes of someFunc. Just as someFunc | |
cannot access inner's variables, inner cannot access inner2's variables | |
(and vice versa) | |
## Scope Tree | |
Looking at the nesting from top-down, a tree of scopes is formed. | |
This code | |
function someFunc() { | |
function inner() { | |
} | |
function inner2() { | |
function foo() { | |
} | |
} | |
} | |
Produces this tree | |
someFunc() | |
| | |
/ \ | |
/ \ | |
/ \ | |
↓ ↓ | |
inner() inner2() | |
| | |
↓ | |
foo() | |
Remembering that inner scopes can access outer scope's variables, but not | |
vice-versa (foo() can access inner2()'s variables, and inner2() can access | |
someFunc()'s variables), then it makes more sense to look at the tree from | |
bottom-up, which forms a chain, also known as... | |
## Scope Chains | |
Looking from most inner to most outer scope forms a Scope Chain. | |
someFunc() | |
↑ | |
\ | |
\ | |
\ | |
inner2() | |
↑ | |
| | |
foo() | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 3 | |
/*Starting with your solution from the previous lesson, assign a value to the global variable | |
quux inside foo() (don't use var or let). Create a shadow variable in of quux | |
inside zip(). The value in the global variable quux has to be different than the | |
value of quux inside zip().*/ | |
function foo() { | |
var bar; | |
quux = 2; | |
function zip() { | |
quux = 1; | |
} | |
} | |
/*The scope chain of the solution looks like this: | |
(global) | |
quux | |
↑ | |
| | |
foo() | |
var bar | |
↑ | |
| | |
zip() | |
var quux | |
Following the arrows, we can see that the quux assigned inside foo() has | |
become globally scoped. This is a different quux from the one inside zip(), | |
which now shadows the globally scoped quux. | |
Understanding where Scope Chains end is an important part of scoping. All | |
Javascript runtimes must implicitly create a Global Scope object (window in | |
the browser, global in node), which sits at the top of every scope chain: | |
(global) | |
↑ | |
| | |
someFunc() | |
↑ | |
/ \ | |
/ \ | |
/ \ | |
inner() inner2() | |
↑ | |
| | |
foo() | |
In Scopes we covered how usage of var or let dictates the scope of the | |
variable being defined. When assigning a variable without using either of var, | |
let, etc, the variable is assumed to exist in an outer scope. | |
The javascript runtime follows these steps to assign a variable: | |
1) Search within the current scope. | |
2) If not found, search in the immediately outer scope. | |
3) If found, go to 6. | |
4) If not found, repeat 2 and 3 until the Global Scope is reached. | |
5) If not found in Global Scope, create it (on window / global objects). | |
6) Assign the value. | |
In this way, it is possible to accidentally define a global variable (step 5). | |
### Example Global Scope | |
Consider the following example: | |
function someFunc() { | |
var scopedVar = 1; | |
function inner() { | |
foo = 2; | |
} | |
} | |
Note the lack of var or let, etc for foo = 2. The Javascript runtime will | |
follow the above algorithm, first checking the scope of inner(), then of | |
someFunc(), then finally the Global Scope. Step 5 is then executed, so foo | |
becomes a variable in the Global Scope (window.foo / global.foo). | |
Phrased another way: By accidentally forgetting to use var, the variable foo | |
which otherwise would have been only within the lexical scope of inner() is | |
now available to be modified by any scope. So, someFunc() now has access | |
where the developer may have meant for it not to. | |
Remember: Only inner scopes can access variables of outer scopes. In this case | |
the someFunc() scope is an inner scope of the Global Scope, allowing access of | |
foo to someFunc(). | |
## Shadowing | |
A variable is created in a 'Step 0)' of the above algorithm: When var or let | |
is used. The variable is assigned to the correct scope, then execution moves on, | |
and any assignments to that variable follow the above algorithm. | |
It is perfectly valid to define two different variables, in different scopes, | |
with the same name: | |
function someFunc() { | |
var foo = 1; | |
} | |
function anotherFunc() { | |
var foo = 2; | |
} | |
It is also valid to do this in nested scopes: | |
function someFunc() { | |
var foo = 1; | |
function inner() { | |
var foo = 2; | |
} | |
} | |
This is called Shadowing. The foo inside inner() is said to Shadow the foo | |
inside someFunc. | |
Shadowing means that the inner() scope only has access to its own foo. There | |
is no way for it to access the foo defined in someFunc(). | |
This can also be an accidental source of bugs, especially when there is deep | |
nesting, or long functions.*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 4 | |
/*Modify your solution from the previous lesson to set bar = true inside zip(), | |
then return the function zip as the result of foo()*/ | |
function foo() { | |
var bar; | |
quux = 2; | |
function zip(){ | |
var quux = 1; | |
bar = true; | |
} | |
return zip; | |
} | |
/*Let's look at the scope chain for your solution: | |
foo() | |
var bar | |
return zip | |
↑ | |
| | |
zip() | |
bar = true | |
By referencing bar within zip, we have created a Closure where zip() closes over | |
the variable bar from its parent scope foo(). | |
Since we are returning the function zip, the reference to bar is maintained | |
(and hence the closure is maintained) until zip is no longer required. | |
Closures are an important part of the Javascript language. They are what enables | |
the callback-last programming most prominent in node, and provide an excellent | |
mechanism for handling the asynchronous nature of most Javascript tasks. | |
To properly understand closures, let's start with an example scope chain: | |
someFunc() | |
↑ | |
| | |
inner() | |
↑ | |
| | |
foo() | |
Let's say someFunc() declares a variable bar: | |
someFunc() | |
var bar | |
↑ | |
⋮ | |
Given how nesting scope works, it's possible for an inner scope within | |
someFunc() to access bar. In this example, let's say inner() accesses | |
bar: | |
someFunc() | |
var bar | |
↑ | |
| | |
inner() | |
alert(bar) | |
↑ | |
⋮ | |
Then inner() is said to Close Over bar. Therefore inner() is a Closure. | |
To power the callback style of programming, the closure will be maintained even | |
if inner() isn't executed immediately. It is perfectly legal in Javascript to | |
pass inner around / return it from someFunc() for later execution. All the | |
while, bar will continue to be available.*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 5 | |
/*In this challenge, you will be required to use Chrome DevTools for detecting | |
Garbage Collection events. Follow these steps to get a feel for what happens | |
when Chrome performs its Mark & Sweep algorithm: | |
1) Fire up a new tab in Chrome | |
2) Open the DevTools > Timeline tab | |
3) Ensure the settings are like so: http://i.imgur.com/RMovIw4.png | |
a) Frames View is unselected (allows seeing memory graphs) | |
b) Flame Chart View is selected (allows seeing where execution time is spent) | |
c) Only "Memory" is selected from the options | |
4) Click the solid gray record button to begin capturing data | |
5) Visit http://www.stackoverflow.com (or your favourite website) | |
6) Click the now-red record button to stop capturing data | |
7) You should now see something similar to: http://i.imgur.com/ZCNMrI1.png | |
8) The part we're interested in is when memory suddenly drops: | |
http://i.imgur.com/FyMyRVI.png | |
9) Click this drop in memory to select it | |
10) Now look for the yellow event called "GC Event": http://i.imgur.com/3ieSxIZ.png | |
11) Clicking this event will reveal information about total memory garbage | |
collected, and how long it took. | |
One particularly interesting thing of note here is the length of time Garbage | |
Collection can take: Often well beyond the 16ms maximum required to keep it | |
within a single frame (at 60fps). While garbage collection occurs, it blocks the | |
main thread, which means other Javascript cannot be executed until the event | |
completes. Be conscious of how janky your application may become due to | |
extensive Garbage Collection events! | |
*/ | |
/** | |
* Garbage Collection | |
Memory in Javascript is managed automatically by the runtime. The runtime | |
decides when/if to release any allocated memory. This decision process is called | |
Garbage Collection. | |
Every javascript runtime has their own algorithm for garbage collection, but | |
most use a variation of Mark & Sweep. The Mark & Sweep algorithm works by | |
marking references to memory (variables, functions, etc) which are still | |
reachable from active code. Any reference which is not marked, is swept into | |
the garbage (i.e. the memory is freed). | |
This concept of marking reachable memory is particularly relevant to closures: | |
someFunc() | |
var bar | |
return inner | |
↑ | |
| | |
inner() | |
alert(bar) | |
↑ | |
⋮ | |
When the closure inner() is returned from someFunc(), it maintains its | |
reference to bar. The Mark & Sweep algorithm will mark bar as reachable, and | |
hence will not garbage collect it. | |
For inner() to correctly resolve its reference to bar, not only does the | |
memory for bar need to be kept, but the scope chain which describes how to | |
reach bar must also be kept. | |
Once the reference to inner() is no longer required, it can be marked for | |
garbage collection, which in turn means bar can also be marked, and finally | |
the entire scope chain can be marked, resulting in the freeing of all the | |
memory. | |
In this way, Scope, Scope Chains, Closures, and Garbage Collection are all | |
closely related. | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment