JavaScript is a peculiar language and some of its design flaws make for a truly amusing time for the interviewer and some rip-out-your-hair moments for the people interviewing. So in order to create that too good to be true **“Thug Life” **moment in front of the interviewer, have a look at these problems.
Question 1. setTimeout inside a for loop
This question was first asked in Google interviews and now many companies have started asking questions on this concept. Have a look at the problem.
const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log('Index: ' + i + ', element: ' + arr[i]);
}, 3000);
}
What do you think the output is?
Is it this?
*Index: 1, element: 10
Index: 2, element: 12
Index: 3, element: 15
Index: 4, element: 21*
Of course not. It couldn't have been that obvious, could it!!
The output actually is this.
*Index: 4, element: undefined // printed 4 times*
The anomaly here is occurring because of the weird nature of JavaScript scope and a concept called 'closures'. Check out this article if you're not familiar with these concepts.
Let's understand the logic behind this weird output. In JS, whenever a new function is created, a closure is also formed which gives the function access to the outer scope.
We have an anonymous function here inside setTimeout in which a closure will be created which will give the function access to the variable 'ifrom the for loop. The function will be called 4 times since there are 4 iterations in the loop. But the problem here is that this function will be called after 3000ms as is mentioned in the 2nd argument of setTimeout. So at the time of execution of this function, the variable 'i' has already incremented to 4.
The key difference is that the first function call will only take place after the loop has already finished and that's why we're seeing the value of 'ias 4.
Let's reshape this program a little bit so that we can visualize it in the browser console.
const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
function log() {
console.log('Index: ' + i + ', element: ' + arr[i]);
}
console.dir(log)
setTimeout(log, 1000);
}
Now we have added a named function log instead of an anonymous function so that we can check out its scope using console.dir
You can see very clearly that the closure created has the value of i as 4.
Now how can we solve this problem? Well, there are 2 ways in which we can do this.
First, we can use the IIFE design to bind the value of the counter in the loop to a local variable in the method. Let's see how we can do that.
Please check out the IIFE design pattern if you haven't seen it before. You may follow this fantastic article on the subject.
const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
setTimeout((function() {
console.log('Index: ' + i + ', element: ' + arr[i]);
})(i), 3000);
}
So instead of an anonymous function, we have added an IIFE with the counter 'ias an argument. So, this stores the updated value as a parameter to the function.
Second, we can use the let variable declaration.
const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log('Index: ' + i + ', element: ' + arr[i]);
}, 3000);
}
Why does this work? The reason is that letis a block-scoped declaration and it will create a new variable binding for every iteration of the for loop. So in each iteration, a new variable with the updated value is being created in the memory.
I know. I didn't see it coming either.
Question 2. Write a function mul(x)(y)(z) to multiply x, y and z.
This question looks scary at first but is rather routine if you use the functional programming pattern of JS, where you can return a function from a function.
Now this problem has 3 separate sets of arguments. So, we need 3 separate functions to consume the arguments and finish the job. Let's do this.
function mul(x) {
return function(y) {
return function(z) {
return x*y*z;
}
}
}
console.log(mul(2)(3)(4)); //output is 24
I know this seems rather easy after seeing the solution. You can use this for n number of separate arguments
Question 3. Function vs variable hoisting.
Check out this program
function func() {
abc = "Variable";
function abc() {
return("Function")
}
return abc();
}
console.log(func());
What do you think is the answer? No, it, of course, is not “function”.
You will actually get the following error
I know. Don't rip out your hair just yet. This is actually related to a concept in JavaScript called hoisting. There are 2 types of hoisting in JS. Variable hoisting and function hoisting.
In variable hoisting, only the variable declarations, and not the variable definition/assignment, is moved to the top of the scope chain.
In function hoisting, both the function declaration as well as the definition is moved to the top of the scope chain.
I recommend you to read the concept of hoisting in detail before proceeding ahead. Check out this great article on the concept: What is hoisting in JavaScript?
Now, in this case, both variable and function have the same name. When this happens, the function will be hoisted to the top and the variable hoisting is ignored. So when the return statement executes, it will return the immediately preceding definition which will be the variable.
This is how the program will look like after hoisting.
function func() {
function abc() {
return("Function")
}
abc = "Variable";
return abc();
}
console.log(func());
I hope this clears everything out and you've not already gone bald at this point.
Question 4. The semi-colon aberration
See the following program
function func1() {
return {
name: "Akarshan"
};
}
function func2() {
return
{
name: "Akarshan"
};
}
console.log(func1())
console.log(func2())
Ideally, both of these functions should return the exact same thing but of course, they don't. That's why we are discussing this.
The output would actually be this
This is because in JavaScript, semi-colons are optional and JavaScript automatically insert a semi-colon at the end of a line when needed. So in the second case, the curly bracket is added in the next line and JavaScript automatically adds a semi-colon at the end of the return statement and thus, the value is '**undefined **in the second function.
Question 5. Event loops and call stacks
Check out the following program
console.log("1")
setTimeout(function() {
console.log("2")
}, 0)
console.log("3")
The output is not 1,2,3 but is rather 1,3,2.
This question is based on how our browsers handle asynchronous and synchronous operations.
Normally, the synchronous functions are executed in the event loop of the browser. But anytime, the browser encounters an asynchronous function, it is added to the call stack. The call stack waits until it can push the function to the event loop to be executed. This only happens once the event loop is empty.
In our case, even though the setTimeout is set to 0, the function is asynchronous in nature and thus, is added to the call stack. The event loop is not empty until it prints “3”. Only after that, does the call stack push the function in the event loop and **“2” **is printed.
Please check out the JavaScript Concurrency Model if you want to check out this concept in detail.
I hope I have helped some of you in need and hopefully, I'll be seeing some new *“Starting a new position at ….” *on my Linkedin Feed. I'll be posting a part 2 with some more interesting problems. Until then. Ciao.