Compiler Speak
With awareness of the two-phase processing of a JS program (compile, then execute), let’s turn our attention to how the JS engine identifies variables and determines the scopes of a program as it is compiled.
First, let’s examine a simple JS program to use for analysis over the next several chapters:
var students = [
{ id: 14, name: "Kyle" },
{ id: 73, name: "Suzy" },
{ id: 112, name: "Frank" },
{ id: 6, name: "Sarah" }
];
function getStudentName(studentID) {
for (let student of students) {
if (student.id == studentID) {
return student.name;
}
}
}
var nextStudent = getStudentName(73);
console.log(nextStudent);
// Suzy
Other than declarations, all occurrences of variables/identifiers in a program serve in one of two “roles”: either they’re the target of an assignment or they’re the source of a value.
(When I first learned compiler theory while earning my computer science degree, we were taught the terms “LHS” (aka, target) and “RHS” (aka, source) for these roles, respectively. As you might guess from the “L” and the “R”, the acronyms mean “Left-Hand Side” and “Right-Hand Side”, as in left and right sides of an =
assignment operator. However, assignment targets and sources don’t always literally appear on the left or right of an =
, so it’s probably clearer to think in terms of target / source rather than left / right.)
How do you know if a variable is a target? Check if there is a value that is being assigned to it; if so, it’s a target. If not, then the variable is a source.
For the JS engine to properly handle a program’s variables, it must first label each occurrence of a variable as target or source. We’ll dig in now to how each role is determined.
Targets
What makes a variable a target? Consider:
students = [ // ..
This statement is clearly an assignment operation; remember, the var students
part is handled entirely as a declaration at compile time, and is thus irrelevant during execution; we left it out for clarity and focus. Same with the nextStudent = getStudentName(73)
statement.
But there are three other target assignment operations in the code that are perhaps less obvious. One of them:
for (let student of students) {
That statement assigns a value to student
for each iteration of the loop. Another target reference:
getStudentName(73)
But how is that an assignment to a target? Look closely: the argument 73
is assigned to the parameter studentID
.
And there’s one last (subtle) target reference in our program. Can you spot it?
..
..
..
Did you identify this one?
function getStudentName(studentID) {
A function
declaration is a special case of a target reference. You can think of it sort of like var getStudentName = function(studentID)
, but that’s not exactly accurate. An identifier getStudentName
is declared (at compile time), but the = function(studentID)
part is also handled at compilation; the association between getStudentName
and the function is automatically set up at the beginning of the scope rather than waiting for an =
assignment statement to be executed.
NOTE: |
---|
This automatic association of function and variable is referred to as “function hoisting”, and is covered in detail in Chapter 5. |
Sources
So we’ve identified all five target references in the program. The other variable references must then be source references (because that’s the only other option!).
In for (let student of students)
, we said that student
is a target, but students
is a source reference. In the statement if (student.id == studentID)
, both student
and studentID
are source references. student
is also a source reference in return student.name
.
In getStudentName(73)
, getStudentName
is a source reference (which we hope resolves to a function reference value). In console.log(nextStudent)
, console
is a source reference, as is nextStudent
.
NOTE: |
---|
In case you were wondering, id , name , and log are all properties, not variable references. |
What’s the practical importance of understanding targets vs. sources? In Chapter 2, we’ll revisit this topic and cover how a variable’s role impacts its lookup (specifically, if the lookup fails).