diff --git a/bin/scope-chains-closures b/bin/scope-chains-closures new file mode 100755 index 0000000..9bd62a7 --- /dev/null +++ b/bin/scope-chains-closures @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +require('../index.js').execute(process.argv.slice(2)); diff --git a/exercises/closures/fail.md b/exercises/closures/en/fail.md similarity index 100% rename from exercises/closures/fail.md rename to exercises/closures/en/fail.md diff --git a/exercises/closures/pass.md b/exercises/closures/en/pass.md similarity index 100% rename from exercises/closures/pass.md rename to exercises/closures/en/pass.md diff --git a/exercises/closures/problem.md b/exercises/closures/en/problem.md similarity index 55% rename from exercises/closures/problem.md rename to exercises/closures/en/problem.md index 2dff9c0..9f3e2dd 100644 --- a/exercises/closures/problem.md +++ b/exercises/closures/en/problem.md @@ -1,8 +1,4 @@ -# Closures - -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. +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: @@ -25,9 +21,7 @@ someFunc() ⋮ ``` -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`: +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() @@ -42,17 +36,12 @@ 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. +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. ---- # Your Mission -Modify your solution from the previous lesson to set `bar = true` inside `zip()`, -then return the function `zip` as the result of `foo()` +Modify your solution from the previous lesson to set `bar = true` inside `zip()`, then return the function `zip` as the result of `foo()` -Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your -solution. +Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your solution. diff --git a/exercises/closures/solution.md b/exercises/closures/en/solution.md similarity index 100% rename from exercises/closures/solution.md rename to exercises/closures/en/solution.md diff --git a/exercises/closures/ja/fail.md b/exercises/closures/ja/fail.md new file mode 100644 index 0000000..5d6f9f4 --- /dev/null +++ b/exercises/closures/ja/fail.md @@ -0,0 +1,12 @@ +# Almost there! + +Check the errors above to see where you went wrong. + +# Hints + + * The task is to return the function `zip`, not the result of `zip()`. + * The value you set to `bar` isn't important. + +# More Help + + * If you're still having troubles, post a question in the nodeschool issues repository: http://bit.ly/scope-chains-question diff --git a/exercises/closures/ja/pass.md b/exercises/closures/ja/pass.md new file mode 100644 index 0000000..df938f3 --- /dev/null +++ b/exercises/closures/ja/pass.md @@ -0,0 +1,4 @@ +# Success! + +Awesome stuff - you closed over the variable `bar` inside `zip`, then returned +`zip`. diff --git a/exercises/closures/ja/problem.md b/exercises/closures/ja/problem.md new file mode 100644 index 0000000..9f3e2dd --- /dev/null +++ b/exercises/closures/ja/problem.md @@ -0,0 +1,47 @@ +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. + +---- + +# Your Mission + +Modify your solution from the previous lesson to set `bar = true` inside `zip()`, then return the function `zip` as the result of `foo()` + +Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your solution. diff --git a/exercises/closures/ja/solution.md b/exercises/closures/ja/solution.md new file mode 100644 index 0000000..b3b8450 --- /dev/null +++ b/exercises/closures/ja/solution.md @@ -0,0 +1,27 @@ +---- + +# Solution + +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. + +This has interesting implications for memory, which we will cover in the next lesson. + +---- + +# Next lesson + +Execute `$ADVENTURE_COMMAND` to move on to the next lesson: _Garbage Collection_. diff --git a/exercises/garbage-collection/fail.md b/exercises/garbage-collection/en/fail.md similarity index 98% rename from exercises/garbage-collection/fail.md rename to exercises/garbage-collection/en/fail.md index b30f303..fb0e750 100644 --- a/exercises/garbage-collection/fail.md +++ b/exercises/garbage-collection/en/fail.md @@ -1,7 +1,6 @@ # Unknown error..? -Congratulations! If you've made it to this screen, you deserve a nyan cat as a -reward :D +Congratulations! If you've made it to this screen, you deserve a nyan cat as a reward :D ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄░░░░░░░░░ diff --git a/exercises/garbage-collection/pass.md b/exercises/garbage-collection/en/pass.md similarity index 100% rename from exercises/garbage-collection/pass.md rename to exercises/garbage-collection/en/pass.md diff --git a/exercises/garbage-collection/problem.md b/exercises/garbage-collection/en/problem.md similarity index 56% rename from exercises/garbage-collection/problem.md rename to exercises/garbage-collection/en/problem.md index b9e6193..15d9059 100644 --- a/exercises/garbage-collection/problem.md +++ b/exercises/garbage-collection/en/problem.md @@ -1,14 +1,6 @@ -# 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_. -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). +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 particulary relevant to closures: @@ -24,29 +16,19 @@ return inner ⋮ ``` -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. +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. +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. +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. +In this way, Scope, Scope Chains, Closures, and Garbage Collection are all closely related. ---- # Your Mission -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: +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 @@ -65,12 +47,6 @@ when Chrome performs its Mark & Sweep algorithm: 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! +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! -**Note**: If you'd like to get that lovely `[COMPLETED]` label for this lesson, -Run `$ADVENTURE_COMMAND verify` +**Note**: If you'd like to get that lovely `[COMPLETED]` label for this lesson, Run `$ADVENTURE_COMMAND verify` diff --git a/exercises/garbage-collection/solution.md b/exercises/garbage-collection/en/solution.md similarity index 56% rename from exercises/garbage-collection/solution.md rename to exercises/garbage-collection/en/solution.md index 19aeb35..96c4607 100644 --- a/exercises/garbage-collection/solution.md +++ b/exercises/garbage-collection/en/solution.md @@ -2,5 +2,4 @@ # Congratulations! -Now that you know all about Scope Chains, Closures, & Garbage Collection, why -not tweet about it: `http://bit.ly/sccjs-twitter-share` +Now that you know all about Scope Chains, Closures, & Garbage Collection, why not tweet about it: `http://bit.ly/sccjs-twitter-share` diff --git a/exercises/garbage-collection/index.js b/exercises/garbage-collection/index.js index 3757829..4729577 100644 --- a/exercises/garbage-collection/index.js +++ b/exercises/garbage-collection/index.js @@ -1,7 +1,6 @@ var problem = require('../../problem'); module.exports = { - title: 'Garbage Collection', problem: problem(__dirname, function (args, t) { t.end(); }) diff --git a/exercises/garbage-collection/ja/fail.md b/exercises/garbage-collection/ja/fail.md new file mode 100644 index 0000000..fb0e750 --- /dev/null +++ b/exercises/garbage-collection/ja/fail.md @@ -0,0 +1,18 @@ +# Unknown error..? + +Congratulations! If you've made it to this screen, you deserve a nyan cat as a reward :D + +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ +░░░░░░░░░░▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄░░░░░░░░░ +░░░░░░░░▄▀░░░░░░░░░░░░▄░░░░░░░▀▄░░░░░░░ +░░░░░░░░█░░▄░░░░▄░░░░░░░░░░░░░░█░░░░░░░ +░░░░░░░░█░░░░░░░░░░░░▄█▄▄░░▄░░░█░▄▄▄░░░ +░▄▄▄▄▄░░█░░░░░░▀░░░░▀█░░▀▄░░░░░█▀▀░██░░ +░██▄▀██▄█░░░▄░░░░░░░██░░░░▀▀▀▀▀░░░░██░░ +░░▀██▄▀██░░░░░░░░▀░██▀░░░░░░░░░░░░░▀██░ +░░░░▀████░▀░░░░▄░░░██░░░▄█░░░░▄░▄█░░██░ +░░░░░░░▀█░░░░▄░░░░░██░░░░▄░░░▄░░▄░░░██░ +░░░░░░░▄█▄░░░░░░░░░░░▀▄░░▀▀▀▀▀▀▀▀░░▄▀░░ +░░░░░░█▀▀█████████▀▀▀▀████████████▀░░░░ +░░░░░░████▀░░███▀░░░░░░▀███░░▀██▀░░░░░░ +░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ diff --git a/exercises/garbage-collection/ja/pass.md b/exercises/garbage-collection/ja/pass.md new file mode 100644 index 0000000..e05d4fc --- /dev/null +++ b/exercises/garbage-collection/ja/pass.md @@ -0,0 +1 @@ +# Everything Looks Good Here... diff --git a/exercises/garbage-collection/ja/problem.md b/exercises/garbage-collection/ja/problem.md new file mode 100644 index 0000000..15d9059 --- /dev/null +++ b/exercises/garbage-collection/ja/problem.md @@ -0,0 +1,52 @@ +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 particulary 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. + +---- + +# Your Mission + +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! + +**Note**: If you'd like to get that lovely `[COMPLETED]` label for this lesson, Run `$ADVENTURE_COMMAND verify` diff --git a/exercises/garbage-collection/ja/solution.md b/exercises/garbage-collection/ja/solution.md new file mode 100644 index 0000000..96c4607 --- /dev/null +++ b/exercises/garbage-collection/ja/solution.md @@ -0,0 +1,5 @@ +---- + +# Congratulations! + +Now that you know all about Scope Chains, Closures, & Garbage Collection, why not tweet about it: `http://bit.ly/sccjs-twitter-share` diff --git a/exercises/global-scope-and-shadowing/fail.md b/exercises/global-scope-and-shadowing/en/fail.md similarity index 100% rename from exercises/global-scope-and-shadowing/fail.md rename to exercises/global-scope-and-shadowing/en/fail.md diff --git a/exercises/global-scope-and-shadowing/pass.md b/exercises/global-scope-and-shadowing/en/pass.md similarity index 100% rename from exercises/global-scope-and-shadowing/pass.md rename to exercises/global-scope-and-shadowing/en/pass.md diff --git a/exercises/global-scope-and-shadowing/problem.md b/exercises/global-scope-and-shadowing/en/problem.md similarity index 55% rename from exercises/global-scope-and-shadowing/problem.md rename to exercises/global-scope-and-shadowing/en/problem.md index 603caea..d6af867 100644 --- a/exercises/global-scope-and-shadowing/problem.md +++ b/exercises/global-scope-and-shadowing/en/problem.md @@ -1,10 +1,6 @@ -# Global Scope & Shadowing - ## Global Scope -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: +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) @@ -21,9 +17,7 @@ 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. +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: @@ -49,28 +43,18 @@ function someFunc() { } ``` -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`). +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. +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()`._ +_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. +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: +It is perfectly valid to define two different variables, in different scopes, with the same name: ```js function someFunc() { @@ -92,24 +76,17 @@ function someFunc() { } ``` -This is called _Shadowing_. The `foo` inside `inner()` is said to _Shadow_ the `foo` -inside `someFunc`. +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()`. +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 can also be an accidental source of bugs, especially when there is deep nesting, or long functions. ---- # Your Mission -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()`. +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()`. -Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your -solution. +Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your solution. diff --git a/exercises/global-scope-and-shadowing/solution.md b/exercises/global-scope-and-shadowing/en/solution.md similarity index 100% rename from exercises/global-scope-and-shadowing/solution.md rename to exercises/global-scope-and-shadowing/en/solution.md diff --git a/exercises/global-scope-and-shadowing/index.js b/exercises/global-scope-and-shadowing/index.js index 6ef6deb..774c712 100644 --- a/exercises/global-scope-and-shadowing/index.js +++ b/exercises/global-scope-and-shadowing/index.js @@ -4,7 +4,6 @@ var fs = require('fs'), asciiScope = require('../../util/ascii-scope'); module.exports = { - title: 'Global Scope & Shadowing', problem: problem(__dirname, function (args, t) { var file = path.resolve(args[0]); diff --git a/exercises/global-scope-and-shadowing/ja/fail.md b/exercises/global-scope-and-shadowing/ja/fail.md new file mode 100644 index 0000000..635acb4 --- /dev/null +++ b/exercises/global-scope-and-shadowing/ja/fail.md @@ -0,0 +1,12 @@ +# Almost there! + +Check the errors above to see where you went wrong. + +# Hints + + * Modifying your solution to lesson 2, _Scope Chains_, is a good start. + * Don't use `var` or `let` when assigning the value to `quux` inside `foo()` + +# More Help + + * If you're still having troubles, post a question in the nodeschool issues repository: http://bit.ly/scope-chains-question diff --git a/exercises/global-scope-and-shadowing/ja/pass.md b/exercises/global-scope-and-shadowing/ja/pass.md new file mode 100644 index 0000000..6706d0d --- /dev/null +++ b/exercises/global-scope-and-shadowing/ja/pass.md @@ -0,0 +1,4 @@ +# Success! + +You assigned a value to `quux`, even though `foo()` doesn't have access to the +`quux` inside `zip()`. diff --git a/exercises/global-scope-and-shadowing/ja/problem.md b/exercises/global-scope-and-shadowing/ja/problem.md new file mode 100644 index 0000000..1425521 --- /dev/null +++ b/exercises/global-scope-and-shadowing/ja/problem.md @@ -0,0 +1,92 @@ + +## Global Scope + +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. 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: + +```js +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: + +```js +function someFunc() { + var foo = 1; +} +function anotherFunc() { + var foo = 2; +} +``` + +It is also valid to do this in nested scopes: + +```js +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. + +---- + +# Your Mission + +Starting with your solution from the previous lesson, assign a value to `quux` inside `foo()` (don't use `var` or `let`). The value should be different to the value assigned when defining `quux` inside `zip()`. + +Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your solution. diff --git a/exercises/global-scope-and-shadowing/ja/solution.md b/exercises/global-scope-and-shadowing/ja/solution.md new file mode 100644 index 0000000..ada00b6 --- /dev/null +++ b/exercises/global-scope-and-shadowing/ja/solution.md @@ -0,0 +1,28 @@ +---- + +# Solution + +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`. + +---- + +# Next lesson + +Execute `$ADVENTURE_COMMAND` to move on to the next lesson: _Closures_. diff --git a/exercises/scope-chains/fail.md b/exercises/scope-chains/en/fail.md similarity index 100% rename from exercises/scope-chains/fail.md rename to exercises/scope-chains/en/fail.md diff --git a/exercises/scope-chains/pass.md b/exercises/scope-chains/en/pass.md similarity index 100% rename from exercises/scope-chains/pass.md rename to exercises/scope-chains/en/pass.md diff --git a/exercises/scope-chains/problem.md b/exercises/scope-chains/en/problem.md similarity index 73% rename from exercises/scope-chains/problem.md rename to exercises/scope-chains/en/problem.md index f5cf54d..1003f96 100644 --- a/exercises/scope-chains/problem.md +++ b/exercises/scope-chains/en/problem.md @@ -1,5 +1,3 @@ -# Scope Chains - ## Nesting Scopes can be nested. Both Lexical and Block scopes can contain other scopes: @@ -36,8 +34,7 @@ function 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. +All nested scopes follow the same rule: Each nested inner scope has access to outer scope variables, but *NOT* vice-versa. For example: @@ -54,10 +51,7 @@ 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. +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: ```js @@ -68,9 +62,7 @@ function someFunc() { } } ``` -*`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)* +*`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 @@ -102,10 +94,7 @@ 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... +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 @@ -128,8 +117,6 @@ Looking from most inner to most outer scope forms a *Scope Chain*. # Your Mission -Modify your solution from lesson 1 so `foo` contains a function `zip` -which itself contains one variable lexically scoped called `quux` +Modify your solution from lesson 1 so `foo` contains a function `zip` which itself contains one variable lexically scoped called `quux` -Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your -solution. +Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your solution. diff --git a/exercises/scope-chains/solution.md b/exercises/scope-chains/en/solution.md similarity index 90% rename from exercises/scope-chains/solution.md rename to exercises/scope-chains/en/solution.md index caa1d0f..f6b717e 100644 --- a/exercises/scope-chains/solution.md +++ b/exercises/scope-chains/en/solution.md @@ -16,8 +16,7 @@ The scope chain you created now looks like this: var quux ``` -By following the arrows, we can see `zip()` has access to `var bar`, but not the -other way around. +By following the arrows, we can see `zip()` has access to `var bar`, but not the other way around. ---- diff --git a/exercises/scope-chains/index.js b/exercises/scope-chains/index.js index 6ae0858..f74f9c3 100644 --- a/exercises/scope-chains/index.js +++ b/exercises/scope-chains/index.js @@ -4,7 +4,6 @@ var fs = require('fs'), asciiScope = require('../../util/ascii-scope'); module.exports = { - title: 'Scope Chains', problem: problem(__dirname, function (args, t) { var file = path.resolve(args[0]); diff --git a/exercises/scope-chains/ja/fail.md b/exercises/scope-chains/ja/fail.md new file mode 100644 index 0000000..5563801 --- /dev/null +++ b/exercises/scope-chains/ja/fail.md @@ -0,0 +1,12 @@ +# Almost there! + +Check the errors above to see where you went wrong. + +# Hints + + * Modifying your solution to lesson 1, _Scopes_, is a good start. + * Ensure you've named your variables correctly (`foo`, `bar`, `zip`, & `quux`). + +# More Help + + * If you're still having troubles, post a question in the nodeschool issues repository: http://bit.ly/scope-chains-question diff --git a/exercises/scope-chains/ja/pass.md b/exercises/scope-chains/ja/pass.md new file mode 100644 index 0000000..7cad8d2 --- /dev/null +++ b/exercises/scope-chains/ja/pass.md @@ -0,0 +1,3 @@ +# Success! + +You created a scope chain using lexical scoping and `var` statements! diff --git a/exercises/scope-chains/ja/problem.md b/exercises/scope-chains/ja/problem.md new file mode 100644 index 0000000..1003f96 --- /dev/null +++ b/exercises/scope-chains/ja/problem.md @@ -0,0 +1,122 @@ +## Nesting + +Scopes can be nested. Both Lexical and Block scopes can contain other scopes: + +```js +function someFunc() { + function inner() { + } +} +``` +*`inner` is a nested lexical scope inside the lexical scope of `someFunc`* + +---- + +```js +if (true) { + while (false) { + } +} +``` +*The `while` is a nested block scope inside the block scope of `if`* + +---- + +```js +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: + +```js +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: +```js +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 + +```js +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() +``` + + +---- + +# Your Mission + +Modify your solution from lesson 1 so `foo` contains a function `zip` which itself contains one variable lexically scoped called `quux` + +Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your solution. diff --git a/exercises/scope-chains/ja/solution.md b/exercises/scope-chains/ja/solution.md new file mode 100644 index 0000000..f6b717e --- /dev/null +++ b/exercises/scope-chains/ja/solution.md @@ -0,0 +1,25 @@ +---- + +# Solution + +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. + +---- + +# Next lesson + +Execute `$ADVENTURE_COMMAND` to move on to the next lesson: _Global Scope & Shadowing_. diff --git a/exercises/scopes/fail.md b/exercises/scopes/en/fail.md similarity index 100% rename from exercises/scopes/fail.md rename to exercises/scopes/en/fail.md diff --git a/exercises/scopes/pass.md b/exercises/scopes/en/pass.md similarity index 100% rename from exercises/scopes/pass.md rename to exercises/scopes/en/pass.md diff --git a/exercises/scopes/problem.md b/exercises/scopes/en/problem.md similarity index 62% rename from exercises/scopes/problem.md rename to exercises/scopes/en/problem.md index c4f4d46..9871cfb 100644 --- a/exercises/scopes/problem.md +++ b/exercises/scopes/en/problem.md @@ -1,20 +1,14 @@ # Scope Chains And Closures Workshop -Scope, Scope Chains, Closures, and Garbage Collection all have one thing in -common: They're often hand-waved away. How do closures actually work? When does -Garbage Collection occur? What really IS a Scope Chain? +Scope, Scope Chains, Closures, and Garbage Collection all have one thing in common: They're often hand-waved away. How do closures actually work? When does Garbage Collection occur? What really IS a Scope Chain? -In this workshop, we will discover it's not black magic after all; No hand -waving is required to explain these language features, in fact you've been using -them all along without realising. +In this workshop, we will discover it's not black magic after all; No hand waving is required to explain these language features, in fact you've been using them all along without realising. ---- -# Scopes +## __{currentExercise.name}__ (_{progress.state_resolved}_) -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] +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] @@ -38,8 +32,7 @@ function someFunc() { ### Block Scope -`let` & `const` are used to denote variables which are Block Scoped to the -current curly braced block: +`let` & `const` are used to denote variables which are Block Scoped to the current curly braced block: ```js if (true) { @@ -53,14 +46,11 @@ if (true) { # Your Mission -In an empty file, create a function `foo` which contains one variable lexically -scoped named `bar`. +In an empty file, create a function `foo` which contains one variable lexically scoped named `bar`. -Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your -solution. +Once complete, execute `$ADVENTURE_COMMAND verify ` to verify your solution. ## 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. + * [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. diff --git a/exercises/scopes/solution.md b/exercises/scopes/en/solution.md similarity index 100% rename from exercises/scopes/solution.md rename to exercises/scopes/en/solution.md diff --git a/exercises/scopes/index.js b/exercises/scopes/index.js index c72c869..d1ce06e 100644 --- a/exercises/scopes/index.js +++ b/exercises/scopes/index.js @@ -3,27 +3,28 @@ var fs = require('fs'), problem = require('../../problem'), asciiScope = require('../../util/ascii-scope'); -module.exports = { - title: 'Scopes', - problem: problem(__dirname, function (args, t) { +var exercise = problem(__dirname, function (args, t) { + var file = path.resolve(args[0]); - var file = path.resolve(args[0]); + fs.readFile(file, function(err, code) { - fs.readFile(file, function(err, code) { + t.error(err, 'Solution loaded'); - t.error(err, 'Solution loaded'); + scopeAsAscii = asciiScope(code); - scopeAsAscii = asciiScope(code); + t.equal( + scopeAsAscii, + ['(global)','\tfoo()','\t- var bar'].join('\n'), + 'The structure is correct' + ); - t.equal( - scopeAsAscii, - ['(global)','\tfoo()','\t- var bar'].join('\n'), - 'The structure is correct' - ); + t.end(); - t.end(); + }); +}); - }); +exercise.header = ''; - }) +module.exports = { + problem: exercise } diff --git a/exercises/scopes/ja/fail.md b/exercises/scopes/ja/fail.md new file mode 100644 index 0000000..8f83c50 --- /dev/null +++ b/exercises/scopes/ja/fail.md @@ -0,0 +1,13 @@ +# おしい! + +エラーメッセージを確認してどこが間違っているかチェックしましょう。 + +# ヒント + + * 正しいスコープを持った変数とするために`var`を使っているか確認しましょう。 + * ウォーミングアップですので考えすぎないようにしましょう。 + * 変数名が正しいか確認しましょう(`foo` や `bar`)。 + +# ヘルプ + + * 問題が解決しない場合は、nodeschool issuesリポジトリに質問してみましょう。:http://bit.ly/scope-chains-question diff --git a/exercises/scopes/ja/pass.md b/exercises/scopes/ja/pass.md new file mode 100644 index 0000000..2b3817b --- /dev/null +++ b/exercises/scopes/ja/pass.md @@ -0,0 +1,3 @@ +# 正解! + +`foo()`関数をレキシカルスコープとする変数`bar`を作ることができました。いいね! diff --git a/exercises/scopes/ja/problem.md b/exercises/scopes/ja/problem.md new file mode 100644 index 0000000..2816cd3 --- /dev/null +++ b/exercises/scopes/ja/problem.md @@ -0,0 +1,56 @@ +# Scope Chains And Closures Workshop + +スコープ、スコープチェーン、クロージャ、そしてガーベジコレクション。 +これらに共通するのは。。。「なんとなくわかるようで、よくわからない」という点。 +クロージャは実際どう動いているの? +ガーベジコレクションはいつ起動されるの? +スコープチェーンっていったい何者? +実際誰も気づかない間に使っているこのあやふやなモノをワークショップを通してクリアにしていきましょう。 + +---- + +## __{currentExercise.name}__ (_{progress.state_resolved}_) + +JavaScriptでスコープといえば基本的に「レキシカルスコープ」になります。「関数」というスコープは初歩的であり開発者にとってはイメージしやすいでしょう。[1] + +最近、ES6では中括弧でスコープを定義する「ブロックスコープ」という概念も生まれました。[2] + +## 変数の初期化 + +スコープの種類によって変数の初期化のしかたが変わります。 + +### レキシカルスコープ + +関数内をスコープとする変数は`var`を使って宣言します。 + +```js +function someFunc() { + var aVariable; +} +``` + +*この例で`aVariable`は`someFunc`関数のレキシカルスコープとなります。* + +### ブロックスコープ + +中括弧でかこまれた範囲をスコープとする変数は`let` や `const`で宣言します。 + +```js +if (true) { + let aVariable; +} +``` + +*この例で`aVariable` は`if`構文内のブロックスコープとなります* + +---- + +# 課題 + +空ファイルの中に「レキシカルスコープ変数`bar`を持つ`foo`関数」を作りましょう。 +作り終えたら `$ADVENTURE_COMMAND verify `を実行し答え合わせしましょう。 + +## メモ + + * [1]: JavaScriptは他に、Global, `with`, `catch`, `eval`という4つのスコープを持っていますが、あまり使われることはないのでここでは省略します。 + * [2]: このワークショップはレキシカルスコープのみを取り扱います。 diff --git a/exercises/scopes/ja/solution.md b/exercises/scopes/ja/solution.md new file mode 100644 index 0000000..b05fd95 --- /dev/null +++ b/exercises/scopes/ja/solution.md @@ -0,0 +1,15 @@ +---- + +# 模範解答 + +あなたの解答と比べてみましょう。 + +```js +function foo() { + var bar; +} +``` + +# 次のレッスン + +`$ADVENTURE_COMMAND`を実行し次のレッスン _Scope Chains_ に進みましょう。 diff --git a/i18n/ja.json b/i18n/ja.json new file mode 100644 index 0000000..769de61 --- /dev/null +++ b/i18n/ja.json @@ -0,0 +1,9 @@ +{ + "exercise": { + "Scopes": "Scopes", + "Scope Chains": "Scope Chains", + "Global Scope & Shadowing": "Global Scope & Shadowing", + "Closures": "Closures", + "Garbage Collection": "Garbage Collection" + } +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..6d5205e --- /dev/null +++ b/index.js @@ -0,0 +1,28 @@ +'use strict'; + +var adventure = require('workshopper-adventure/adventure'); + +var shop = adventure({ + appDir: __dirname, + languages: ['en', 'ja'], + header: require('workshopper-adventure/default/header'), + footer: [ + {text: '---', type: 'md'}, + require('workshopper-adventure/default/footer') + ] +}) + +;[ + 'Scopes', + 'Scope Chains', + 'Global Scope & Shadowing', + 'Closures', + 'Garbage Collection' +].forEach(function(name, index) { + shop.add(name, function() { + var folder = name.replace(/\s/ig, '-').replace(/\&/ig, 'and') + return require('./exercises/' + folder).problem + }); + }) + +module.exports = shop diff --git a/package.json b/package.json index 4099f56..e48c505 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,10 @@ }, "engineStrict": true, "preferGlobal": true, - "repository": "jesstelford/scope-chains-closures", + "repository": { + "type": "git", + "url": "https://github.com/jesstelford/scope-chains-closures.git" + }, "bugs": "https://github.com/jesstelford/scope-chains-closures/issues", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -27,11 +30,10 @@ "author": "Jess Telford (http://jes.st)", "license": "MIT", "dependencies": { - "adventure": "^2.10.0", "adventure-verify": "^2.2.0", "babel-core": "^5.4.3", "escope": "^1.0.3", "estraverse": "^4.1.0", - "msee": "^0.1.1" + "workshopper-adventure": "^4.0.4" } } diff --git a/problem.js b/problem.js index d9d7788..23b0cd2 100644 --- a/problem.js +++ b/problem.js @@ -1,19 +1,18 @@ -var msee = require('msee'), - path = require('path'), +var path = require('path'), verify = require('adventure-verify'); module.exports = function(dir, testCorrect) { var problem = { + init: function (exercise) { + ['problem', 'solution', 'pass', 'fail'].forEach(function(type) { + problem[type] = { + file: path.join(dir, exercise.i18n.lang(), type + '.md') + } + }); + }, verify: verify({ modeReset: true }, testCorrect) }; - ['problem', 'solution', 'pass', 'fail'].forEach(function(type) { - problem[type] = msee.parseFile( - dir + '/' + type + '.md', - {paragraphEnd: '\n\n'} - ); - }); - return problem; }; diff --git a/runner.js b/runner.js deleted file mode 100755 index db1f51d..0000000 --- a/runner.js +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env node - -'use strict'; - -var packageJson = require('./package.json'), - adventure = require('adventure'); - -var shop = adventure(packageJson.name), - lesson; - -[ - 'scopes', - 'scope-chains', - 'global-scope-and-shadowing', - 'closures', - 'garbage-collection' -].forEach(function(lesson, index) { - - lesson = require('./exercises/' + lesson); - - shop.add((index + 1) + '. ' + lesson.title, function() { - return lesson.problem - }); -}) - -shop.execute(process.argv.slice(2));