Interviews

12 useful JavaScript interview tips

1. What is a `Set’ object and how does it work? Set object allows you to store unique values of any type, whether...

Written by Luci · 9 min read >
1. What is a `Set’ object and how does it work?

Set object allows you to store unique values of any type, whether they are primitive values or object references.

We can create a Set instance using the Set constructor.

const set1 = new Set();
const set2 = new Set(["a","b","c","d","d","e"]);

We can use the add method to add a new value to a Set instance. Since the add method returns the Set object, we can chain multiple add calls together. If a value already exists in the Set object, it will not be added again

set2.add("f");
set2.add("g").add("h").add("i").add("j").add("k").add("k");

We can use the has method to check if a specific value exists in a Set instance.

set2.has("a") // true
set2.has("z") // true

We can use the size property to get the length of a Set instance.

set2.size // returns 10

The clear method can be used to remove all data from a Set.

set2.clear();

We can use a Set object to remove duplicate elements from an array.

const numbers = [1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 5];
const uniqueNums = [...new Set(numbers)]; // [1,2,3,4,5,6,7,8]
2.What is a callback function?

Callback function is an executable code snippet that is passed as an argument to other code. Its purpose is to be invoked at a convenient time by the receiving code when needed.

In JavaScript, functions are a type of objects. Just like objects, functions can be passed as arguments to other functions. Therefore, a function that is passed as an argument to another function is called a callback function.

const btnAdd = document.getElementById('btnAdd');

btnAdd.addEventListener('click', function clickCallback(e) {
    // do something useless
});

In this example, we are waiting for the click event on the element with the id btnAdd. If it is clicked, the function clickCallback will be executed. The callback function adds some functionality to certain data or events.

The reducefilter, and map methods in arrays require a callback as a parameter. A good analogy for a callback is when you call someone, and if they don’t answer, you leave a message, expecting them to call you back. The act of calling someone or leaving a message is the event or data, and the callback is the operation you expect to happen later.

3. What is ES6 module?

Modules allow us to split our codebase into multiple files for better maintainability and to avoid having all the code in one large file. Before ES6 introduced support for modules, there were two popular module systems.

  • CommonJS-Node.js
  • AMD (Asynchronous Module Definition) — Browser

Basically, using modules is quite simple. import is used to retrieve functionality or multiple functionalities or values from another file, while export is used to expose functionality or multiple functionalities or values from a file.

Export

Using ES5 (CommonJS)

// useing ES5 CommonJS - helpers.js
exports.isNull = function (val) {
  return val === null;
}

exports.isUndefined = function (val) {
  return val === undefined;
}
exports.isNullOrUndefined = function (val) {
  return exports.isNull(val) || exports.isUndefined(val);
}

Using ES6 modules

// Using ES6 Modules - helpers.js
export function isNull(val){
  return val === null;
}

export function isUndefined(val) {
  return val === undefined;
}
export function isNullOrUndefined(val) {
  return isNull(val) || isUndefined(val);
}

Importing functions from another file

//ES5 (CommonJS) - index.js
const helpers = require('./helpers.js'); // helpers is an object
const isNull = helpers.isNull;
const isUndefined = helpers.isUndefined;
const isNullOrUndefined = helpers.isNullOrUndefined;
// or if your environment supports Destructuring
const { isNull, isUndefined, isNullOrUndefined } = require('./helpers.js');
-------------------------------------------------------
// ES6 Modules - index.js
import * as helpers from './helpers.js'; // helpers is an object
// or 
import { isNull, isUndefined, isNullOrUndefined as isValid } from './helpers.js';
// using "as" for renaming named exports

Exporting a single function or default export in a file

ES5 (CommonJS)

// ES5 (CommonJS) - index.js
class Helpers {
  static isNull(val) {
    return val === null;
  }

static isUndefined(val) {
    return val === undefined;
  }
  static isNullOrUndefined(val) {
    return this.isNull(val) || this.isUndefined(val);
  }
}
module.exports = Helpers;

Using ES6 Modules

// using ES6 Modules - helpers.js
class Helpers {
  static isNull(val) {
    return val === null;
  }

static isUndefined(val) {
    return val === undefined;
  }
  static isNullOrUndefined(val) {
    return this.isNull(val) || this.isUndefined(val);
  }
}
export default Helpers

Importing a single function from another file

Using ES5 (CommonJS)

//ES5 (CommonJS) - index.js
const Helpers = require('./helpers.js'); 
console.log(Helpers.isNull(null));

Using ES6 Modules

import Helpers from '.helpers.js'
console.log(Helpers.isNull(null));
4. What is a Promise?

Promise is a solution for asynchronous programming. Syntax-wise, promise is an object from which you can obtain the outcome of an asynchronous operation. Conceptually, it represents a commitment to provide a result after a certain period of time. promise has three states: pending fulfilled and rejected . Once the state changes, it remains unchanged. After creating a Promise instance, it executes immediately.

fs.readFile('somefile.txt', function (e, data) {
  if (e) {
    console.log(e);
  }
  console.log(data);
});

If we have another asynchronous operation within a callback, it leads to a problem. We end up with messy and unreadable code. This code is known as ‘callback hell’.

// callback hell
fs.readFile('somefile.txt', function (e, data) {
  //your code here
  fs.readdir('directory', function (e, files) {
    //your code here
    fs.mkdir('directory', function (e) {
      //your code here
    })
  })
})

If we use promisein this code, it will be more readable, understandable, and maintainable.

promReadFile('file/path')
  .then(data => {
    return promReaddir('directory');
  })
  .then(data => {
    return promMkdir('directory');
  })
  .catch(e => {
    console.log(e);
  })

promise has three distinct states:

  • pending:the initial state, the state before fulfillment or rejection.
  • fulfilled:Operation completed successfully
  • rejected:Operation failed

pending object in the pending state triggers the fulfilled/rejected state, passing the resolved value/error message in its respective state handling methods. When the operation is successfully completed, the then method of the Promise object is invoked. Otherwise, the catch method is triggered. For example:

const myFirstPromise = new Promise((resolve, reject) => {
    setTimeout(function(){
        resolve("Success!"); 
    }, 250);
});

myFirstPromise.then((data) => {
    console.log("Yay! " + data);
}).cat
5. What is async/await and how does it work?

async/await is a new method in JavaScript for writing asynchronous or non-blocking code. It is built on top of Promises and provides higher readability and conciseness for asynchronous code.

async/await is a new method in JavaScript for writing asynchronous or non-blocking code. It is built on top of Promises and provides higher readability and conciseness compared to Promises and callbacks. However, before using this feature, it is necessary to learn the basics of Promises because, as mentioned earlier, async/await is built on top of Promises, which means it still utilizes Promises behind the scenes.

Promise

function callApi() {
  return fetch("url/to/api/endpoint")
    .then(resp => resp.json())
    .then(data => {
      //do something with "data"
    }).catch(err => {
      //do something with "err"
    });
}

async/await

In async/await, we use the try/catch syntax to catch exceptions.

async function callApi() {
  try {
    const resp = await fetch("url/to/api/endpoint");
    const data = await resp.json();
    //do something with "data"
  } catch (e) {
    //do something with "err"
  }
}

Note: Using the ‘async’ keyword to declare a function implicitly returns a Promise.

const giveMeOne = async () => 1;

giveMeOne()
  .then((num) => {
    console.log(num); // logs 1
  });

Note: await keyword can only be used inside an async function. Using the await keyword in any non-async function will throw an error. await keyword waits for the right-hand expression (which can be a Promise) to return before executing the next line of code.

const giveMeOne = async () => 1;

function getOne() {
  try {
    const num = await giveMeOne();
    console.log(num);
  } catch (e) {
    console.log(e);
  }
}
// Uncaught SyntaxError: await is only valid in async function
async function getTwo() {
  try {
    const num1 = await giveMeOne(); 
    const num2 = await giveMeOne(); 
    return num1 + num2;
  } catch (e) {
    console.log(e);
  }
}
await getTwo(); // 2
6. What is the difference between the spread operator and the rest operator?

The spread operator is denoted by three dots  and it can convert an array into a comma-separated sequence of arguments. In simpler terms, it is similar to breaking apart a large element into individual smaller elements, like how a palm strike disperses a solid object.

The rest operator, also denoted by three dots , may look similar to the spread operator, but it is used for destructuring arrays and objects. In a way, the rest operator is the opposite of the spread operator. While the spread operator ‘spreads’ an array into multiple elements, the rest operator ‘collects’ multiple elements and ‘compresses’ them into a single element.

function add(a, b) {
  return a + b;
};

const nums = [5, 6];
const sum = add(...nums);
console.log(sum);

In this example, we used the spread operator when calling the add function, which expanded the nums array. Therefore, the value of parameter a is 5, and the value of parameter b is 6, resulting in the sum being 11.

function add(...rest) {
  return rest.reduce((total,current) => total + current);
};

console.log(add(1, 2)); // 3
console.log(add(1, 2, 3, 4, 5)); // 15

In this example, we have an add function that accepts any number of parameters, adds them all together, and then returns the total sum.

const [first, ...others] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(others); // [2,3,4,5]

Here, we use the rest operator to extract all the remaining array values and put them into another array, excluding the first item.

7. What are default parameters?

Default parameters are a new way to define default variables in JavaScript, available in ES6 or ECMAScript 2015.

//ES5 Version
function add(a,b){
  a = a || 0;
  b = b || 0;
  return a + b;
}

//ES6 Version
function add(a = 0, b = 0){
  return a + b;
}
add(1); // returns 1

We can also use destructuring in default parameters.

function getFirst([first, ...rest] = [0, 1]) {
  return first;
}

getFirst();  // 0
getFirst([10,20,30]);  // 10
function getArr({ nums } = { nums: [1, 2, 3, 4] }){
    return nums;
}
getArr(); // [1, 2, 3, 4]
getArr({nums:[5,4,3,2,1]}); // [5,4,3,2,1]

We can also use previously defined parameters before defining subsequent parameters.

function doSomethingWithValue(value = "Hello World", callback = () => { console.log(value) }) {
  callback();
}
doSomethingWithValue(); //"Hello World"
8. What is a wrapper object?

Now let’s review the data types in JavaScript. JavaScript data types are divided into two major categories: primitive types and reference types.

Primitive types: Undefined,Null,Boolean,Number,String,Symbol,BigInt

reference types: Object,Array,Date,RegExp,etc. In simple terms, they are objects.

Among reference types, there are methods and properties, which are not available in primitive types. However, we often come across the following code:

let name = "maxwell";

console.log(typeof name); // "string"
console.log(name.toUpperCase()); // "MAXWELL"

The name type is a string and belongs to the primitive type, so it doesn’t have any properties or methods. However, in this example, we are calling the toUpperCase() method, which doesn’t throw an error and returns the uppercase value of the string.

The reason is that the value of a primitive type is temporarily converted or coerced into an object, so the behavior of the name variable is similar to that of an object. Every primitive type, except for null and undefined, has its own wrapper object: StringNumberBooleanSymbol, and BigInt. In this case, name.toUpperCase() behind the scenes looks like this:

console.log(new String(name).toUpperCase()); // "MAXWELL"

After accessing properties or invoking methods, the newly created object is immediately discarded.

9. What is the difference between implicit and explicit type conversion?

Implicit type conversion is a method of converting a value to another type, which is done automatically without manual intervention.

Let’s assume we have the following example below.

console.log(1 + '6'); // 16
console.log(false + true); // 1
console.log(6 * '2'); // 12

The result of the first console.log statement is 16. In other languages, this would throw a compilation error, but in JavaScript, is converted to a string and then concatenated with the + operator. We didn’t do anything; it is automatically done by JavaScript.

The result of the second console.log statement is 1. In JavaScript, false is converted to a boolean value of 0, while true is converted to 1. Therefore, the result is 1.

The result of the third console.log statement is 12. It converts ‘2’ to a number and then multiplies it by 6 * 2, resulting in 12.

Explicit type coercion, on the other hand, is a method of converting a value to another type where we need to manually perform the conversion.

console.log(1 + parseInt('6'));

In this example, we use the parseInt function to convert ‘6’ to a number, and then we use the + operator to add 1 and 6 together.

10. What is NaN? And how do you check if a value is NaN?

NaN, short for “Not a Number,” is a value in JavaScript that arises as a result of numeric operations or conversions that cannot produce a meaningful numeric value. Therefore, when a numeric operation or conversion yields a non-numeric value, the result is NaN.

let a;

console.log(parseInt('abc')); // NaN
console.log(parseInt(null)); // NaN
console.log(parseInt(undefined)); // NaN
console.log(parseInt(++a)); // NaN
console.log(parseInt({} * 10)); // NaN
console.log(parseInt('abc' - 2)); // NaN
console.log(parseInt(0 / 0)); // NaN
console.log(parseInt('10a' * 10)); // NaN

JavaScript has a built-in isNaN method used to test if a value is NaN. However, this function exhibits a peculiar behavior.

console.log(isNaN()); // true
console.log(isNaN(undefined)); // true
console.log(isNaN({})); // true
console.log(isNaN(String('a'))); // true
console.log(isNaN(() => { })); // true

All of these console.log statements return true, even if the values we pass are not NaN.

In ES6, it is recommended to use the Number.isNaN method because it genuinely checks if the value is NaN. Alternatively, we can create our own helper function to check this issue, as in JavaScript, NaN is the only value that is not equal to itself.

function checkIfNaN(value) {
  return value !== value;
}
11. How can you determine if a value is an array?

We can use the Array.isArray method to check if a value is an array. When passed an argument that is an array, it returns true; otherwise, it returns false.

console.log(Array.isArray(5));  // false
console.log(Array.isArray("")); // false
console.log(Array.isArray()); // false
console.log(Array.isArray(null)); // false
console.log(Array.isArray({ length: 5 })); // false

console.log(Array.isArray([])); // true

If the environment doesn’t support this method, you can implement a polyfill.

function isArray(value){
 return Object.prototype.toString.call(value) === "[object Array]"
}

Of course, you can also use traditional methods:

let a = []
if (a instanceof Array) {
  console.log('is an array')
} else {
  console.log('Non-Arrays')
}
12. How can you check if a property exists in an object?

There are three methods to check if a property exists in an object.

The first method is to use the in operator:

const o = { 
  "prop" : "rabbit",
  "prop2" : "tiger"
};

console.log("prop" in o); // true
console.log("prop1" in o); // false

The second method is to use the hasOwnProperty method. The hasOwnProperty() method returns a boolean value indicating whether the object has the specified property as a direct property (not inherited).

console.log(o.hasOwnProperty("prop2")); // true
console.log(o.hasOwnProperty("prop1")); // false

The third method is to use the bracket notation obj[‘prop’]. If the property exists, it will return the value of that property; otherwise, it will return undefined.

console.log(o["prop"]); // "rabbit"
console.log(o["prop1"]); // undefined
Written by Luci
I am a multidisciplinary designer and developer with a main focus on Digital Design and Branding, located in Cluj Napoca, Romania. Profile

Javascript interview questions

Luci in Interviews
  ·   3 min read