Permalink
Please sign in to comment.
Browse files
Rewrite article on JS scopes
The previous article barely had any description and wasn't very helpful. #367
- Loading branch information...
95
js-Scopes.md
@@ -0,0 +1,95 @@ | ||
+# Scopes in JavaScript | ||
+ | ||
+If you've been programming in JavaScript for a while, you've undoubtedly run into a concept known as `scope`. What is `scope`? Why should you take the time to learn it? | ||
+ | ||
+In programmer speak, `scope` is the **current context of execution**. Confused? Let's take a look at the following piece of code: | ||
+ | ||
+```js | ||
+1. var foo = 'Hi, I am foo!'; | ||
+2. | ||
+3. var baz = function () { | ||
+4. var bar = 'Hi, I am bar too!'; | ||
+5. console.log(foo); | ||
+6. } | ||
+7. | ||
+8. baz(); // Hi, I am foo! | ||
+9. console.log(bar); // ReferenceError... | ||
+``` | ||
+ | ||
+This is a simple example, but it does a good job of illustrating what is known as *Lexical scope*. JavaScript, and almost every other programming language has a *Lexical scope*. There is another kind of scope known as *Dynamic scope*, but we won't be discussing that. | ||
+ | ||
+Now, the term *Lexical scope* sounds fancy, but as you will see it's really simple in principle. In a Lexical Scope, there are two kinds of scopes: the *global scope* and a *local scope*. | ||
+ | ||
+Before you type the first line of code in your program, a *global scope* is created for you. This contains all the variables that you declare in your program **outside any functions**. | ||
+ | ||
+In the example above, the variable `foo` is in the global scope of the program, while the variable `bar` is declared inside a function and is therefore **in the local scope of that function**. | ||
+ | ||
+Lets break down the example line by line. While you might be confused at this point, I promise you will have a much better understanding by the time you finish reading this. | ||
+ | ||
+On line 1 we are declaring the variable `foo`. Nothing too fancy here. Lets call this a left-hand size (LHS) reference to `foo`, because we are assigning a value to `foo` and it's on the left-hand side of the `equal` sign. | ||
+ | ||
+On line 3, we are declaring a function and assigning it to variable `baz`. This is another LHS reference to `baz`. We are assigning a value to it (remember, functions are values too!). This function is then called on line 8. This is a RHS, or a right-hand side reference to `baz`. We are retrieveing `baz`'s value, which in this case is a function and then invoking it. Another RHS reference to `baz` would be if we assigned its value to another variable, for example `foo = baz`. This would be a LHS reference to `foo` and a RHS reference to `baz`. | ||
+ | ||
+The LHS and RHS references might sound confusing, but they are important for discussing scope. Think of it this way: a LHS reference is assigning a value to the variable, while a RHS reference is retrieving the value of the variable. They're just a shorter and more convenient way of saying 'retrieving the value' and 'assigning a value'. | ||
+ | ||
+Let's now breakdown what's happening inside the function itself. | ||
+ | ||
+When the compiler compiles the code inside a function, it enters the function's **local scope**. | ||
+ | ||
+On line 4, the variable `bar` is declared. This is a LHS reference to `bar`. On the next line, we have a RHS reference to `foo` inside the `console.log()`. Remember, we are retrieving `foo`'s value and then passing it as an argument to the method `console.log()`. | ||
+ | ||
+When we have a RHS reference to `foo`, the compiler looks for the declaration of the variable `foo`. The compiler doesn't find it in the function itself, or the **function's local scope** so it **goes up one level: to the global scope**. | ||
+ | ||
+At this point you're probably thinking that scope has something to do with variables. That is correct. A scope can be thought of as a container for variables. All variables that are created within a local scope are only accessible in that local scope. However, all local scopes can access the global scope. (I know you're probably even more confused right now, but just bear with me for a few more paragraphs). | ||
+ | ||
+So the compiler goes up to the global scope to find a LHS reference to the variable `foo`. It finds one on line 1, so it retrieves the value from the LHS reference, which is a string: `'Hi, I am foo!'`. This string is sent to the `console.log()` method, and outputted to the console. | ||
+ | ||
+The compiler has finished executing the code inside the function, so we come back out to line 9. On line 9, we have a RHS reference for the variable `bar`. | ||
+ | ||
+Now, `bar` was declared in the local scope of `baz`, but there is a RHS reference for `bar` in the global scope. Since there is no LHS reference for `bar` in the global scope, the compiler can't find a value for `bar` and throws a ReferenceError. | ||
+ | ||
+But, you might ask, if the function can look outside itself for variables, or a local scope can peek into the global scope to find LHS references, why can't the global scope peek into a local scope? Well that's how lexical scope works! | ||
+ | ||
+```js | ||
+... // global scope | ||
+var baz = function() { | ||
+ ... // baz's scope | ||
+} | ||
+... /// global scope | ||
+``` | ||
+ | ||
+This is the same code from above which illustrates the scope. This forms a sort of hierarchy that goes up to the global scope: | ||
+ | ||
+ | ||
+` baz -> global `. | ||
+ | ||
+ | ||
+So, if there is a RHS reference for a variable inside `baz`'s scope, it can be fulfilled by a LHS reference for that variable in the global scope. But the opposite is **not true**. | ||
+ | ||
+What if we had another function inside `baz`? | ||
+ | ||
+```js | ||
+... // global scope | ||
+var baz = function() { | ||
+ ... // baz's scope | ||
+ | ||
+ var bar = function() { | ||
+ ... // bar's scope. | ||
+ } | ||
+ | ||
+} | ||
+... /// global scope | ||
+``` | ||
+ | ||
+In this case, the hierarchy or the **scope chain** would look like this: | ||
+ | ||
+ | ||
+`bar -> baz -> global` | ||
+ | ||
+ | ||
+Any RHS references inside `bar`'s local scope can be fullfilled by LHS references in the global scope or `baz`'s scope, but a RHS reference in `baz`'s scope cannot be fullfilled by a LHS reference in `bar`'s scope. | ||
+ | ||
+**You can only traverse down a scope chain, not up.** | ||
+ | ||
+# References | ||
+1. [Scopes and Closures](https://github.com/getify/You-Dont-Know-JS/tree/master/scope%20%26%20closures) by Kyle Simpson. It goes into more details of how the scope mechanism works, and also has a superficial description of how the JavaScript compiler works, so if you're interested in that, definitly give it a read! It's free on GitHub and can be bought from O'Reilly. |
6
js-scope.md
@@ -1,6 +0,0 @@ | ||
-The current context of execution. The context in which values and expressions are "visible," or can be referenced. If a variable or other expression is not "in the current scope," then it is unavailable for use. Scopes can also be layered in a hierarchy, so that child scopes have access to parent scopes, but not vice versa. | ||
- | ||
-**See also:** :arrow_heading_up: [Links](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/js-scope#links) | ||
-## Links | ||
-* [Understanding Scope and Context](http://ryanmorr.com/understanding-scope-and-context-in-javascript/) - Detailed explanation with examples by Ryan Morr | ||
-* [Wikipedia - Scope](https://en.wikipedia.org/wiki/Scope_(computer_science)) |
0 comments on commit
7e748c6