- Published on
During Coding Interviews, There are 3 JavaScript Queries to Watch Out For.
- Authors
- Name
- Harendra Kumar Kanojiya
- @harendraverma2
All current web browsers use JavaScript as their official language. As a result, questions about JavaScript appear in a wide range of developer interviews.
This post isn't about the latest JavaScript libraries, best practises, or new ES6 functions. Rather, it's about three points that frequently come up in JavaScript interviews. These are questions that I've been asked, and my friends have informed me that they've also been asked.
Of course, these aren't the only three things you should study before a JavaScript interview - there are a slew of other things you can do to improve your chances — but here are three questions an interviewer can ask to gauge your knowledge and understanding of the JavaScript language and the DOM.
So let's get this party started! In the examples following, we'll use vanilla JavaScript because the interviewer will often want to assess how well you grasp JavaScript and the DOM without the usage of libraries like jQuery.
Question 1: Event delegation.
When creating an application, you may need to add event listeners to buttons, text, or pictures on the page to do some action when the user interacts with them.
If we take a simple todo list as an example, the interviewer may inform you that when a user selects one of the list items, they want something to happen. They want you to use JavaScript to achieve this feature, assuming the following HTML code:
<ul id="todo-app">
<li class="item">Walk the dog</li>
<li class="item">Pay bills</li>
<li class="item">Make dinner</li>
<li class="item">Code for one hour</li>
</ul>
To link event listeners to the components, you would want to do something like this:
document.addEventListener('DOMContentLoaded', function() {
let app = document.getElementById('todo-app');
let items = app.getElementsByClassName('item');
// attach event listener to each item
for (let item of items) {
item.addEventListener('click', function() {
alert('you clicked on item: ' + item.innerHTML);
});
}
});
While this is technically correct, the issue is that you are separately assigning an event listener to each object. This is excellent for four components, but what if someone adds 10,000 items to their todo list (they may have a lot to do)? Then your function will create 10,000 event listeners, each of which will be attached to the DOM. This isn't really practical.
In an interview, it's advisable to start by asking the interviewer how many items the user may enter at a time. If the number can never exceed ten, for example, the code above will suffice. If the user may enter an unlimited number of things, however, you should use a more efficient technique.
If your application potentially wind up with hundreds of event listeners, attaching one to the entire container and then being able to access each item when it's clicked would be a more efficient option. This is referred to as event delegation, because it is far more efficient than attaching individual event handlers.
The event delegation code is as follows:
document.addEventListener('DOMContentLoaded', function() {
let app = document.getElementById('todo-app');
// attach event listener to whole container
app.addEventListener('click', function(e) {
if (e.target && e.target.nodeName === 'LI') {
let item = e.target;
alert('you clicked on item: ' + item.innerHTML);
}
});
});
Question 2: Using a closure within a loop
Closures are occasionally brought up in interviews so that the interviewer can assess your familiarity with the language and if you know when to use one.
When an inner function has access to variables outside of its scope, it is called a closure. Closures may be used to implement privacy and create function factories, among other things. The following is an example of a popular interview question about the usage of closures:
After a 3-second wait, write a function that loops over a list of numbers and prints the index of each entry.
This is an example of a frequent (incorrect) solution I've seen for this problem:
const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log('The index of this number is: ' + i);
}, 3000);
}
If you execute this, you'll see that instead of the usual 0, 1, 2, 3, the 4 is written every time after a 3 second wait.
It would be helpful to understand why this happens in JavaScript, which is precisely what the interviewer is attempting to test, in order to accurately determine why this is occurring.
Because the setTimeout function produces a function (the closure) with access to its outer scope, which is the loop containing the index I this is the case. The function is called after 3 seconds, and it prints out the value of I which is 4 at the conclusion of the loop since it cycles through 0, 1, 2, 3, 4, and ultimately stops at 4.
There are a couple different methods to write the function for this problem appropriately. Here are two examples:
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);
}
Question 3: Debouncing
Some browser actions, such as resizing a window or scrolling down a website, might fire several times in a short period of time. If you connect an event listener to the window scroll event, for example, and the user scrolls down the page repeatedly and fast, your event might fire thousands of times in less than 3 seconds. This might wreak havoc on your system's performance.
If activities like scrolling, window resizing, or key hitting come up during an interview, make sure to include debouncing and/or throttling as a strategy to enhance page speed and performance. From this guest post on css-tricks, here's a real-life example:
In 2011, a problem with the Twitter website surfaced: scrolling through your Twitter feed became laggy and unresponsive. John Resig wrote a blog article about the issue, explaining why it's a terrible idea to attach costly functions to the scroll event directly.
Debouncing is one solution to this problem, as it reduces the amount of time that must pass before a function is called again. As a result, a proper implementation of debouncing would combine numerous function calls into one and execute it just once after a certain amount of time has passed. Here's a simple JavaScript implementation that uses concepts like scope, closures, this, and timing events:
// debounce function that will wrap our event
function debounce(fn, delay) {
// maintain a timer
let timer = null;
// closure function that has access to timer
return function() {
// get the scope and parameters of the function
// via 'this' and 'arguments'
let context = this;
let args = arguments;
// if event is called, clear the timer and start over
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(context, args);
}, delay);
}
}
When wrapped around an event, this function will only run when a particular period of time has passed. This function would be used as follows:
// function to be called when user scrolls
function foo() {
console.log('You are scrolling!');
}
// wrap our function in a debounce to fire once 2 seconds have gone by
let elem = document.getElementById('container');
elem.addEventListener('scroll', debounce(foo, 2000));
Throttling is a similar method to debouncing, only instead of waiting for a certain amount of time to pass before running a function, throttling simply spreads out the function calls across a larger time interval. Throttling might spread out each of the function calls to be executed once every 2 seconds instead of all firing within 100 milliseconds if an event occurs ten times in 100 milliseconds.