loading...
Cover image for A tricky JavaScript interview question asked by Google
Coderbyte

A tricky JavaScript interview question asked by Google

borowskidaniel profile image Daniel Borowski Updated on ・3 min read

The following will be a short explanation, along with some solutions, of a popular JavaScript question that tends to get asked in developer interviews. The question usually looks something like the following:

// interviewer: what will the following code output?
const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
  setTimeout(function() {
    console.log('Index: ' + i + ', element: ' + arr[i]);
  }, 3000);
}

This question deals with the topics: closures, setTimeout, and scoping.

The correct answer to this is question is:

Index: 4, element: undefined
Index: 4, element: undefined
Index: 4, element: undefined
Index: 4, element: undefined

If that’s not what you expected, then hopefully the rest of this article will help explain why this is the case in JavaScript.

Why is this question so popular?

A user on reddit mentioned that they were asked this question in an Amazon developer interview. I’ve also been asked this type of closure + loop question in interviews myself — even in a Google interview.

This question tests your knowledge of some important JavaScript concepts, and because of how the JavaScript language works this is actually something that can come up quite often when you’re working — namely, needing to use setTimeout or some sort of async function within a loop.

A solid understanding of functional/block scope, anonymous functions, closures, and IIFE’s will definitely make you a better JavaScript developer and help you out in future interviews.

Solutions

I've written about this particular type of challenge on Coderbyte and on freeCodeCamp as well.

The reason for this is because the setTimeout function creates a function (the closure) that has access to its outer scope, which is the loop that contains the index i. After 3 seconds go by, the function is executed and it prints out the value of i, which at the end of the loop is at 4 because it cycles through 0, 1, 2, 3, 4 and the loop finally stops at 4. arr[4] does not exist, which is why you get undefined.

There are two popular solutions to the question. One involves passing the needed parameters into the inner function, and the other solution makes use of ES6.

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
  // pass in the variable i so that each function 
  // has access to the correct index
  setTimeout(function(i_local) {
    return function() {
      console.log('The index of this number is: ' + i_local);
    }
  }(i), 3000);
}
const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
  // using the ES6 let syntax, it creates a new binding
  // every single time the function is called
  // read more here: http://exploringjs.com/es6/ch_variables.html#sec_let-const-loop-heads
  setTimeout(function() {
    console.log('The index of this number is: ' + i);
  }, 3000);
}

A user on the reddit post provided a similar answer to this question as well. There’s also a good explanation of closures by a user on Stack Overflow.

This often confuse people who are new to JavaScript or functional programming. It is a result of misunderstanding what closures are. A closure does not merely pass the value of a variable or even a reference to the variable. A closure captures the variable itself!

Thanks for reading! Check out Coderbyte for some coding practice :)

This article originally appeared on Medium.

Posted on by:

Coderbyte

Coderbyte is a web application built to help you practice programming and improve your coding skills.

Discussion

pic
Editor guide
 

I just strongly dislike the idea that someone's job could be based on how well they understand the intricacies of poorly written and purposely confusing code. I understand you're providing a valuable example of what currently is, but I wish we could do something to change that paradigm. Create some other way of measuring competence in programming ability.

 

I agree with you on the interview process.

On the other hand, this question is great to start a conversation on why old-school JavaScript is bad - JavaScript has good parts, but had lots of flaws as well, most of which were fixed later by EcmaScript 6, Typescript, React, Angular, ...

Edit: clarified my answer

 

Every tool has its uses.

I started my career in Android land which, at the time, requires lots of ceremonial boilerplate to get things going.

Kotlin is a huge improvement. That’s besides the point

I’ve now been working with React and React Native professionally for a couple years and really have become enamored with the ease of iteration.

As an independent developer, shlopping JSON around the application and perhaps providing some required props here and there is all I’ve really needed. We don’t need statically typed languages to write mobile applications.

Would they have helped on some crazy debugging issues I’ve had? Absolutely.

Should I have gone through the hassle of adding another layer of abstraction just because they essentially give me better warnings? Jury is still out. For me personally, I don’t think so.

I am no fan of Android as described here :)

Every tool has its uses.

Yes, and every tool should work on fixing its main flaws. This is basically what was done with JavaScript with both EcmaScript 6 and TypeScript and React and Angular...

 

No-one would dispute that "old-school JavaScript" had lots of flaws; but most developers with a job will have migrated to a "safer" programming language some time ago; namely JavaScript. Believe it or not the language has moved on since Crockford wrote "The Good Parts"; as is obvious from the ES6 solution provided.

And Typescript is not "another programming language" that magically guarantees you are free of the issues illustrated in the example question. It's still just JavaScript. I can still type that problem loop into my editor; Typescript will still compile and it will still produce the same output.

Believe it or not the language has moved on since Crockford wrote "The Good Parts"; as is obvious from the ES6 solution provided

I believe it!
I have edited my original answer, it was not my intention to claim that the current JavaScript ecosystem is the same as "old school Javascript".

 

Agree with you. At first I thought the same thing everyone else did. It would print out the value. But as soon as I saw the index 4, I knew exactly what was happening.

This is the sort of thing you usually don't spot while coding but once you see it happening (usually in test run), is trivial to debug and fix. For one, using let and const over var produces more sensible and predictable results.

 
for (var i = 0; i < arr.length; i++) {
  setTimeout(function(index, element) {
    console.log('Index: ' + index + ', element: ' + element);
  }, 3000, i, arr[i]);
}

will also produce expected output

 

Wow! I didnt know you could pass more arguments to the setTimeout function like that. Good to know, thank you!

 

To me this looks like a useless interview question.

I don't know the answer because I don't need to know it. It's been years since I don't write such code. People doing JS for 15 years are much more likely to know this. New frontend devs who started on ES5/6 would likely never need to learn this.

 

Like the article, but I think it would help more people with a more thorough explanation. The answers don't exactly state where and why you get Index: 4. We forget people who are just learning and skip steps that are obvious to more advanced developers. Saying closure doesn't explain that the 'i' variable continues to increase. I personally never had one teacher explain the details of a for loop. You might never see it without making this mistake.

 

Is not a feature, is a implementation error.

 

Really? Is this a tricky question? This is the first question I ask when interviewing JS devs.