Let’s meet the cast of characters that interact to process the program , so we understand their conversations that we’ll listen in on shortly:
Engine: responsible for start-to-finish compilation and execution of our JavaScript program.
Compiler: one of Engine‘s friends; handles all the dirty work of parsing and code-generation (see previous section).
For you to fully understand how JavaScript works, you need to begin to think like Engine (and friends) think, ask the questions they ask, and answer those questions the same.
When you see the program var a = 2;
, you most likely think of that as one statement. But that’s not how our new friend Engine sees it. In fact, Engine sees two distinct statements, one which Compiler will handle during compilation, and one which Engine will handle during execution.
So, let’s break down how Engine and friends will approach the program var a = 2;
.
The first thing Compiler will do with this program is perform lexing to break it down into tokens, which it will then parse into a tree. But when Compiler gets to code-generation, it will treat this program somewhat differently than perhaps assumed.
A reasonable assumption would be that Compiler will produce code that could be summed up by this pseudo-code: “Allocate memory for a variable, label it a
, then stick the value 2
into that variable.” Unfortunately, that’s not quite accurate.
Compiler will instead proceed as:
Encountering
var a
, Compiler asks Scope to see if a variablea
already exists for that particular scope collection. If so, Compiler ignores this declaration and moves on. Otherwise, Compiler asks Scope to declare a new variable calleda
for that scope collection.Compiler then produces code for Engine to later execute, to handle the
a = 2
assignment. The code Engine runs will first ask Scope if there is a variable calleda
accessible in the current scope collection. If so, Engine uses that variable. If not, Engine looks elsewhere (see nested Scope section below).
If Engine eventually finds a variable, it assigns the value 2
to it. If not, Engine will raise its hand and yell out an error!
To summarize: two distinct actions are taken for a variable assignment: First, Compiler declares a variable (if not previously declared in the current scope), and second, when executing, Engine looks up the variable in Scope and assigns to it, if found.
We need a little bit more compiler terminology to proceed further with understanding.
When Engine executes the code that Compiler produced for step (2), it has to look-up the variable a
to see if it has been declared, and this look-up is consulting Scope. But the type of look-up Engine performs affects the outcome of the look-up.
In our case, it is said that Engine would be performing an “LHS” look-up for the variable a
. The other type of look-up is called “RHS”.
Side… of what? Of an assignment operation.
In other words, an LHS look-up is done when a variable appears on the left-hand side of an assignment operation, and an RHS look-up is done when a variable appears on the right-hand side of an assignment operation.
Actually, let’s be a little more precise. An RHS look-up is indistinguishable, for our purposes, from simply a look-up of the value of some variable, whereas the LHS look-up is trying to find the variable container itself, so that it can assign. In this way, RHS doesn’t really mean “right-hand side of an assignment” per se, it just, more accurately, means “not left-hand side”.
Being slightly glib for a moment, you could also think “RHS” instead means “retrieve his/her source (value)”, implying that RHS means “go get the value of…”.
Let’s dig into that deeper.
When I say:
The reference to a
is an RHS reference, because nothing is being assigned to a
here. Instead, we’re looking-up to retrieve the value of a
, so that the value can be passed to console.log(..)
.
By contrast:
The reference to a
here is an LHS reference, because we don’t actually care what the current value is, we simply want to find the variable as a target for the = 2
assignment operation.
Note: LHS and RHS meaning “left/right-hand side of an assignment” doesn’t necessarily literally mean “left/right side of the assignment operator”. There are several other ways that assignments happen, and so it’s better to conceptually think about it as: “who’s the target of the assignment (LHS)” and “who’s the source of the assignment (RHS)”.
Consider this program, which has both LHS and RHS references:
The last line that invokes foo(..)
as a function call requires an RHS reference to foo
, meaning, “go look-up the value of foo
, and give it to me.” Moreover, (..)
means the value of foo
should be executed, so it’d better actually be a function!
There’s a subtle but important assignment here. Did you spot it?
You may have missed the implied a = 2
in this code snippet. It happens when the value 2
is passed as an argument to the foo(..)
function, in which case the 2
value is assigned to the parameter a
. To (implicitly) assign to parameter a
, an LHS look-up is performed.
There’s also an RHS reference for the value of a
, and that resulting value is passed to console.log(..)
. console.log(..)
needs a reference to execute. It’s an RHS look-up for the console
object, then a property-resolution occurs to see if it has a method called log
.
Finally, we can conceptualize that there’s an LHS/RHS exchange of passing the value 2
(by way of variable a
‘s RHS look-up) into . Inside of the native implementation of log(..)
, we can assume it has parameters, the first of which (perhaps called arg1
) has an LHS reference look-up, before assigning 2
to it.
Note: You might be tempted to conceptualize the function declaration function foo(a) {...
as a normal variable declaration and assignment, such as var foo
and foo = function(a){...
. In so doing, it would be tempting to think of this function declaration as involving an LHS look-up.
Let’s imagine the above exchange (which processes this code snippet) as a conversation. The conversation would go a little something like this:
Check your understanding so far. Make sure to play the part of Engine and have a “conversation” with the Scope:
Identify all the LHS look-ups (there are 3!).