Presenting the essential JavaScript interview question
What may seem like such a simple JavaScript interview question can reveal so much of a candidates understanding of the language.
After having contracted for sometime now, I’ve had the opportunity to sit in on a number of interviews - mostly for developers to replace myself!
But, having recently accepted a permanent role as Lead Developer, it’s now part of my role to interview potential candidates for the junior roles we have in our expanding team.
As such, I’ve put together a series of practical ‘problems’ or ‘exercises’. When interviewing for junior roles, I’ve found it’s much easier for a potential candidate to demonstrate knowledge of concepts like closure, scope, prototype delegation, etc… than it may be for them to verbally articulate it. After all, they are interviewing for a junior role with the intention of growing their knowledge working with the rest of the team.
The first exercise I put forward is always the same, as I will show in just a moment, and determines which ones will follow.
I recently posted this on a private message board to get some feedback from other developers - hoping for some constructive criticism. Some was positive, with people trying to explain and solve the problem. A lot was negative, saying that it was a ridiculous question that was contrived and had no practical application. That was, until I explained how and why I always ask it.
🥁 Drum roll please for the problem at hand…
(function() {
const numbers = [0.1, 0.2, 0.3];
let sum = 0;
for (var i = 0; i < numbers.length; ++i) {
setTimeout(() => {
sum += numbers[i];
}, 0);
}
console.log(sum === 0.6);
})();
I have almost no doubt in my mind that many of you reading this will have seen something similar before…and yes, it may seem like a garbage question at first. And, if used in the wrong sitting, it is pretty much meaningless and you will derive nothing of a candidate’s understanding of JavaScript from it.
But, if you will indulge me, I will demonstrate just how valuable this few lines of code actually is in determining an understanding of:
- Variable assignment operators
- Scope and Closure
- The Event Loop
- The Call Stack
- The Message Bus
- The
Number
type (IEEE 754) - Side effects
- Functional programming
Having the candidate walk through the problem with you.
This is where the magic happens. If you don’t walk through it with them - explaining where there may be gaps in their knowledge and asking them to expand on certain concepts - the exercise is worthless.
I will include links along the way to related materials for those that may not be familiar with terms or concepts explored in this simple, but also complex, piece of code.
The Event Loop
This piece of code is opened in a browser and as the event loop ticks over, execution begins…
Read: The JavaScript Event Loop: Explained - Carbon Five
Function Expressions & IIFEs
(function() { .. })();
We have a function wrapped in parentheses. Since parentheses cannot contain a statement, the JavaScript engine knows that what is inside needs to be evaluated as an expression. As such, we have a function expression and not a function declaration.
On the last line we also have both opening and closing parentheses ()
, indicating that an invocation should take place.
Since we are immediatly invoking a function expression, we have an Immediately Invoked Function Expression or IIFE for short.
Read: Ben Alman » Immediately-Invoked Function Expression (IIFE)
Execution Contexts & The Call Stack
Now we are inside of an executing function, we have created a new function execution context and pushed it onto the call stack.
Read: What is the Execution Context & Stack in JavaScript? by David Shariff
Variable Assignment & Identifiers
const numbers = [0.1, 0.2, 0.3];
Inside of our IIFE, on the first line we have a single equals sign =
, indicating an assignment should take place.
The right hand side of the assignment operator is an array containing three numerical values.
The left hand side is the variable name numbers
, to which we are assigning the array.
We are assigning the array to the pointer numbers
using the const
keyword.
The const
keyword indicates that once this assignment has taken place, the numbers
variable name cannot be reassigned.
If this had been assigned to an immutable value, such as the number 23
or the string 'hello world'
, then will always return that value. Since it has been assigned to a mutable value (an array), whilst it cannot be reassigned and will always return that particular array, we can still change the values contained in the array, e.g. numbers.push('apple');
, giving the return value that of [0.1, 0.2, 0.3, 'apple']
.
let sum = 0;
The next line is again, an assignment. This time the right hand side is the number 0
and the variable name on the left sum
.
We are using the let
keyword to make this assignment. Whilst there are some idiosyncrasities involved when using the let
keyword, we can think of it as being a block-scoped variable that cannot be redeclared, but can be reassigned.
Meaning that later on in our code, we can do sum = 23
and sum
will now point to 23
.
Read: JavaScript variable assignment explained - Syntax Success
Read: Understanding let and const in JavaScript ES6 - JavaScript Kit
Read: var, let, or const? - Hacker Noon
Loops & Side Effects
for (var i = 0; i < numbers.length; ++i) { .. }
We now have the opening of a for
loop and its header.
Inside the header we make another assignment, this time initialising the value of 0
to the variable i
using the var
keyword. Unlike let
and const
, the var
keyword is not block scoped, it is function scoped. This means that it is scoped to its nearest outer function, in this case the IIFE, and current execution context we are in.
Next is the condition for the for
loop, where we say that we want the loop to continue executing until the value that i
points to is less than (<
) the value of numbers.length
(the length of the numbers array.
Lastly, for each time the loop executes we increment the value that i
points to by one.
Note, that the value of i
we are incrementing does not exist within the for
loop. It exists in the execution context of the outer function. As such, this incrementation is a side effect of running the loop.
Read: Getting Fancy with the JavaScript For Loop - HTML Goodies
Global Functions & The Message Queue
setTimeout(() => { .. }, 0);
Now we are inside of the for
loop and the first thing we do is use the browser (not JavaScript!) function setTimeout
which is present on the window
or global object.
setTimeout
is a timing function. It is passed two arguments: A callback function to execute, and the delay (in milliseconds) before that function should be invoked.
When setTimeout
is invoked, the callback function is placed onto the message queue, along with the minimum delay after which it should be invoked, in this case 0
milliseconds.
Note that the callback function is not yet invoked, as we are still in our current execution context of the for
loop, and messages on the queue will not be received until the call stack is empty.
Following the logic of the loop, this happens another two times.
Read: How JavaScript Timers Work - Jon Resig
console.log(sum === 0.6);
Since nothing has yet happened to change the value that sum
was assigned (0
), this will print false
in the console, as 0
does not equal 0.6
.
Callbacks, Scope & Closure
sum += numbers[i];
Now our current execution context has exited and been popped off the call stack, the event loop finally gets around to receiving the functions we added to the message queue.
It invokes each one in turn, creating a new execution context for each, pushing and popping it, on to and off of the call stack.
Since we no longer have access to the numbers
array, because it was inside the IIFE whose execution context no longer exists, numbers[i]
returns undefined
.
The first callback function received from the message queue tries to add 0
to undefined
, which results in NaN
. The next two functions received from the message queue try to add undefined
to NaN
, which again results in NaN
.
Ultimately leaving sum
assigned a value of NaN
.
Identifying and solving the problems
This will be covered in my next post Solving the essential JavaScript interview question, coming next… 🙂
There are a numerous ways to solve this problem. If you’ve already done it, I’d love to hear about it.
In the mean time…
If you’re bored in the mean time and find yourself with a half hour spare, this talk by Philip Roberts from JSConf EU 2014 on the JavaScript Event Loop is particularly good watch.
All the links in once place
- The JavaScript Event Loop: Explained - Carbon Five
- Ben Alman » Immediately-Invoked Function Expression (IIFE)
- What is the Execution Context & Stack in JavaScript? by David Shariff
- JavaScript variable assignment explained - Syntax Success
- Understanding let and const in JavaScript ES6 - JavaScript Kit
- var, let, or const? - Hacker Noon
- Getting Fancy with the JavaScript For Loop - HTML Goodies
- How JavaScript Timers Work - Jon Resig
- Solving the essential JavaScript interview question
- Wait for content to be updated and rendered in AngularJS 1.x
- Can you give a realistic task estimate?
- Creating a maintainable gulpfile.js
- JavaScript is a compiled language?
- Integrating ESLint settings with WebStorm
- What are object prototypes? [Part 1]
- Why participate in #100DaysOfCode?
- Finding a decent static site generator
- Now