- "W3Schools", by Brandon Morelli (W3Schools)
- "The battle between function scope and block scope", by Marius Herring (Dead Code Rising)
There’s several types of scopes to know:
- Global Scope - Global scope refers to code can be accessed anywhere else, even separate functions. Global scope should be avoided as much as possible, since it greatly increases the risk of code “leaking out” to unexpected areas with unexpected (and often bad) results.
- Function Scope - Function scope refers to code that can only be accessed in a specific function. Variables declared in a function can only be accessed from within that function. Trying to call these variables from the global scope will result in an
- Block Scope - Block scope was formally introduced with ES6, and refers to code only in certain operation “blocks,” like
ifstatements or for loops. Block scope variables must be declared with
This is generally easy to understand, but can get confusing when you use
var, which isn’t block-scoped. Declaring a variable in the global scope, and declaring another with the same name in a block, will override the global variable since there’s no block scope.
If we used
let instead of
var, the second declaration would be block-scoped and not overwrite the global one. The
console.log would then return
Some key traits of the call stack:
- The call stack is Last In, First Out. Function calls are added to the top of a “pile,” and whatever’s on top of the pile is always called before what’s below it can be called next. It’s similar to stacking dishes - the last one you put on the pile is the first one you use later on.
- A stack overflow is when the call stack limit is exceeded. Recursive functions (functions that call themselves) can trigger a stack overflow when there’s no end coded into it. Whatever’s running this code will ultimately crash.
Closure relates to when variables are declared within certain scopes. When calling a function, any variables it has are created, do their business, and are destroyed afterwards. If you call the function again, it recreates the variables in its scope all over again - all changes and operations from before are forgotten, they’re redone, and then destroyed again.
Closure comes into play when calling a function being stored into a variable. If that function has any variables in its local scope, those values aren’t destroyed once the function is done - they’re saved and remembered for later. Calling this function instance that was saved executes the same operations, but it carries over the remembered values too.
closureExample has created a closure of this function. The
number variable is set at zero, but changes to this variable will be remembered. You can see this if you call this function a few times:
Every time it’s called, it remembers the increased value of
number from before and increases it. A non-closure example of this function would start over at
0 each time and always return
1. A way to remember this is if a bubble closed around these variables and protected them from destruction in this variable so they can keep changing as you keep using it.
Recursions are functions that call themselves. They can be helpful since they can keep code dry, since you only write code once but can execute it as many times as needed. However recursive functions that have no end will cause a stack overflow (see Call Stack), so be sure they’re used properly.
Take this example function that finds a number’s factorial. This multiplies a number by every number lower than it until it returns the total product. So the factorial of
5 would be
5 * 4 * 3 * 2 * 1.
factorial(5) leads to the following recursive function calls:
The part of the function with
(n < 2) ? 1 is crucial, since it’s what stops the function from returning itself. Once the number gets down to
1, it simply returns that without calling itself, stopping the loop. Without it, the function would simply look like this:
And would be played out this way instead:
Even though the result would have to be
0 since the result is being multipled by zero, the important thing is nothing’s telling this function to stop calling itself. It will keep doing so until something in the code tells it through, and since nothing will, it simply goes into it creates a stack overflow.
var declarations and value assignments can be placed anywhere on a page. However, the variable declarations are “hoisted” automatically to the top of the page. This means that any
var doesn’t necessarily need to be declared, since it will be declared when it’s hoisted. A computer reading this:
This will change to this once it gets hoisted:
This is why the first code sample, although it looks invalid, actually becomes valid when it becomes the second version. This isn’t necessarily a good thing, since it can create unexpected complexity or bugs in the code.
Avoiding this is easier with ES6, since the new variable declarations
const are not hoisted.
Double vs Triple Equal Signs
=== comparisons looks for strict equality, meaning it looks for matching “type” and “value”.
55 === 55returns
55 === '55'returns
false(Same values, different types)
== comparisons look for loose equality, meaning it only looks for matching value. It can perform “type coercion” if needed, which converts the both values to the same type before comparing values.
55 == 55returns
55 == '55'returns
true(Different types, but it converts both values to the same type before comparing them, which makes it true)
This distinction is important since loose equality can lead to unexpected bugs related to conditions returning true when they may need to be false. It’s better practice to always use
=== for comparisons.