Introduction, Scope, and Functions
Hello world!
In the past year, I have spent a lot of time and effort to become proficient in JavaScript. But I am slightly concerned that my skills will slowly diminish as I have recently started a new job that works with other languages. With that said, I am happy to announce that I will begin blogging to help me stay sharp.
Full disclosure: I am not a professional writer by any means, so please excuse my poor grammar. If you happen to stumble upon this blog, God bless your soul for trying to find useful information. This blog is not catered towards any particular type of developer. I plan on writing small posts on things that I am learning right now, things that I have learned in the past, and more!
Without further ado, I introduce to you a small but important lesson of JavaScript! This week’s post is a compilation of notes that I have taken from various sources, including You Don’t Know JS and 2ality!
Scope
In JavaScript, scope and closure are both incredibly important but be difficult to learn as a beginner. You can think of scope as a set of rules that determines where and how a variable can be looked-up. This look-up may be for the purposes of assigning to the variable, which is a left-hand-side reference, or it may be for the purposes of retrieving its value, which is an right-hand-side reference.
LHS references result from assignment operations. Scope-related assignments can occur either with the = operator or by passing arguments to function parameters.
The JavaScript Engine first compiles code before it executes, and in so doing, it splits up statements like var a = 2; into two separate steps;
- First, var a to declare it in that Scope.
- Later, a = 2 to look up the variable and assign to it if found.
Both LHS and RHS reference look-ups start at the currently executing Scope, and if need be, they work their way up the nested Scope, one scope at a time, looking for the identifier, until they get to the global and stop, and either find it, or don’t.
Scope look-up stops once it finds the first match. But the same identifier name can be specified at multiple layers of nested scope, which is called “shadowing”. Regardless of shadowing, scope look-up always starts at the innermost scope being executed at the time, and works its way outward/upward until the first match, and stops.
Keep in mind that global variables are also automatically properties of the global object (window in browsers), so it is possible to reference a global variable not directly by its lexical name, but instead indirectly as a property reference of the global objects.
Scope from Functions
In JavaScript, each function you declare creates a scope for itself. While functions are the most common unit of scope, and certainly the most wide-spread of the design approaches in the majority of JS in circulation, other units of scope are possible, and the usage of these other scope units can lead to even better, cleaner to maintain code.
Check this out:
for (var i = 0; i<10; i++) {
console.log(‘hi’);
}We declare the variable i directly inside the for-loop head, most likely because our intent is to use i only within the context of that for-loop, and essentially ignore the fact that the variable actually scopes itself to the enclosing scope.
That’s what block-scoping is all about. Declaring variables as close as possible, as local as possible, to where they will be used.
Function declaration vs. expression
The easiest way to distinguish declaration vs. expression is the position of the word “function” in the statement. If “function” is the very first thing in the statement, then it’s a function declaration. Otherwise, it’s a function expression. The key difference we can observe here between a function declaration and a function expression relates to where its name is bound as an identifier.
In other words, (function foo(){ .. }) as an expression means the identifier foo is found only in the scope where the .. indicates, not in the outer scope. Hiding the name foo inside itself means it does not pollute the enclosing scope unnecessarily.
You are probably most familiar with function expressions as callback parameters, such as:
setTimeout( function() {
console.log(“I waited!”);
}, 1000 );This is called an “anonymous function expression”, because function()… has no name identifier on it. Function expressions can be anonymous, but function declarations cannot omit the name.
To be continued…