Interview questions part 2: JavaScript

in: #interview #javascript
Reading time: 9 minutes
Cover Image for Interview questions part 2: JavaScript

Welcome to the second post on interview questions. You can read previous post which covers general questions regarding HTML, CSS and some of the accessibility.

In this post, I'd like to focus on common JavaScript questions and simple code tests you might be given during the interview. Below list is a mix of various questions that allow interviewers to gauge your seniority level.

What is an IIFE and why are they used?

IIFE stands for: Immediately-invoked function expression

The main reason to use it is to preserve a private scope within your function inside of your JavaScript code you want to make sure that you are not overriding any global variables.

!(() => {
	console.log("Hello from IIFE!");
})();

List Iteration/loops types

  • for
  • while
  • do while
  • for of
  • for in

Explain hoisting

  • Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution.
  • Function expressions load only when the interpreter reaches that line of code. So if you try to call a function expression before it's loaded, you'll get an error!
  • If you call a function declaration instead, it'll always work, because no code can be called until all declarations are loaded.
hoistedFunction(); // Hello! I am defined immediately!
notHoistedFunction(); // ReferenceError: notHoistedFunction is not defined

// Function Decalration
function hoistedFunction () {
  console.log('Hello! I am defined immediately!');
}

// Function Expression
const notHoistedFunction = function () {
  console.log('I am not defined immediately.');
}

List ES6 features

  • arrow functions
  • classes
  • template strings
  • destructing - The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
  • default value
  • spread operator - Spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected. Example: [...iterableObj, '4', 'five', 6];
  • let, const, var

List JavaScript data types

  • Number
  • String
  • Boolean
  • Null
  • Undefined
  • Symbol

How JavaScript's runtime works? Describe an event loop mechanism. How many threads JavaScript has?

Javascript runtime consists of a few parts:

  • Heap - a large mostly unstructured region of memory, where variables are allocated
  • Call stack - where function calls form a stack of frames
  • Queue - a list of messages to be processed. Each message has an associated function that gets called to handle the message.

Functions from the call stack are executed according to the "First in, first out" rule, meaning that the function on top will be executed first.

Async actions such as fetch or setTimeout are provided by the Web API's, and executed by them, so the thread of the JS runtime can remain unblocked while waiting for the timeout or request to be completed. Completed async actions are being put to the queue and pushed back to the call stack once it's empty. This means that setTimeout with 0 timeout might not output the result immediately.

References:

What are Web Workers

Web workers are scripts that run in the background without the page needing to wait for it to complete. It can be useful when you have a heavy-cost, slow operation running in your application as it won't block the JS runtime while it's running and allow a user to interact with the page.

https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers

What is Closure?

A closure is an inner function that has access to the outer (enclosing) function's variables—scope chain. The closure has three scope chains:

  • it has access to its own scope (variables defined between its curly brackets)
  • it has access to the outer function's variables
  • it has access to global variables.
const siteName = 'lukaszpietraszek.com';

function outerFunc() {
  const title = 'Interview Questions';

  function innerFunc() { 
    console.log(siteName);
    console.log(title );
  }
  return innerFunc;
}
const myFunc = outerFunc();

myFunc();
// lukaszpietraszek.com
// Interview Questions

Difference between var and let

  • let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used.
  • var keyword defines a variable globally, or locally to an entire function regardless of block scope.

Difference between map, filter and reduce

  • The Map object holds key-value pairs and remembers the original insertion order of the keys. A Map object iterates its elements in insertion order — a for...of loop returns an array of [key, value] for each iteration.
  • The map() function returns a new array by passing a function over each element in the input array.
  • The filter() method creates a new array with all elements that pass the test implemented by the provided function. A new array with the elements that pass the test. If no elements pass the test, an empty array will be returned.
  • Reduce method of the array object is used to reduce the array to one single value The reduce() method executes a reducer function (that you provide) on each member of the array resulting in a single output value.
  • The reducer function takes four arguments: Accumulator (acc) Current Value (cur) Current Index (idx), Source Array (src). Your reducer function's returned value is assigned to the accumulator, whose value is remembered across each iteration throughout the array and ultimately becomes the final, single resulting value.

Explain how prototypal inheritance works

JavaScript only has one construct: objects. Each object has an internal link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype. null, by definition, has no prototype and acts as the final link in this prototype chain.

  • The core idea of Prototypal Inheritance is that an object can point to another object and inherit all its properties.
  • The main purpose is to allow multiple instances of an object to share common properties, hence, the Singleton Pattern.

Explain Getters and Setters

  • A getter is a method that gets the value of a specific property. A setter is a method that sets the value of a specific property. You can define getters and setters on any predefined core object or user-defined object that supports the addition of new properties. The syntax for defining getters and setters uses the object literal syntax.
  • A difference between using a getter or setter and using a standard function is that getters/setters are automatically invoked on assignment. So it looks just like a normal property but behind the scenes, you can have extra logic (or checks) to be run just before or after the assignment.
const person = {
    firstName: 'John',
    lastName: 'Doe',
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    },
    set fullName(name) {
        const words = name.toString().split(' ');
        this.firstName = words[0] || '';
        this.lastName = words[1] || '';
    }
};

person.fullName = 'Mark Smith';
console.log(person.firstName); // Mark
console.log(person.lastName) // Smith

What is a callback function?

JavaScript functions as arguments, and can be returned by other functions.

Functions that do this are called higher-order functions. Any function that is passed as an argument is called a callback function.

const allUserData = [];

function logStuff(userData) {
  if (typeof userData === "string") {
    console.log(userData);
  } else if (typeof userData === "object") {
    for (const item in userData) {
      console.log(`${item}: ${userData[item]}`);
    }
  }
}

function getInput(options, callback) {
  allUserData.push(options);

  if (typeof callback === "function") {
    callback(options);
  }
}

getInput({ firstName: "John", lastName: "Doe" }, logStuff);
// firstName: John
// lastName: Doe

What are Promises

It allows you to associate handlers with an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point.

A Promise is in one of these states:

  • pending: initial state, neither fulfilled nor rejected.
  • fulfilled: meaning that the operation completed successfully.
  • rejected: meaning that the operation failed.
const promise = new Promise((resolve, reject) => {
  if (/* everything turned out fine */) {
    resolve("Stuff worked!");
  } else {
    reject(Error("It broke"));
  }
});

promise.then(
  result => {
    console.log(result); // "Stuff worked!"
  },
  err => {
    console.log(err); // Error: "It broke"
  },
);

Async/Await

An async function is a modification to the syntax used in writing promises. You can call it syntactic sugar over promises. It only makes writing promises easier.

An async function returns a promise -- if the function returns a value, the promise will be resolved with the value, but if the async function throws an error, the promise is rejected with that value. Let’s see an async function:

Await is only used with an async function. The await keyword is used in an async function to ensure that all promises returned in the async function are synchronized, ie. they wait for each other. Await eliminates the use of callbacks in .then() and .catch(). In using async and await, async is prepended when returning a promise, await is prepended when calling a promise. try and catch are also used to get the rejection value of an async function. Let's see this with our date example:

async function myDate() {
  try {
    let dateDetails = await date;
    let message = await orderUber(dateDetails);
    console.log(message);
  } catch (error) {
    console.log(error.message);
  }
}

What’s the difference between a variable that is: null, undefined or undeclared?

  • Undeclared is any variable that has not been declared yet. Console throws an error for this.
  • Undefined is a declared variable that has no assigned value, yet.
  • Null is a value that has been assigned to a variable.

What is Singleton

Singleton is a pattern that allows you to create one instance of an object. If such an instance already exists, you cannot create a second one. Additionally, the initialization of this object takes place only when it is needed in the program. These are the two most important features of this pattern. If a structure lacks both, it is not a singleton. It is best to imagine Singleton as a module (what it will be anyway) that encapsulates the entire mechanism that initializes an instance of that Singleton.

const mySingleton = (() => {
  // Instance stores a reference to the Singleton
  let instance;
  function init() {
    // Singleton
    // Private methods and variables
    function privateMethod() {
      console.log("I am private");
    }
    const privateVariable = "Im also private";
    return {
      // Public methods and variables
      publicMethod() {
        console.log("The public can see me!");
      },
      publicProperty: "I am also public",
    };
  }

  return {
    // Get the Singleton instance if one exists
    // or create one if it doesn't
    getInstance() {
      if (!instance) {
        instance = init();
      }
      return instance;
    },
  };
})();

// Usage:
const singleA = mySingleton.getInstance();
const singleB = mySingleton.getInstance();

console.log(singleA === singleB); // true

Scope types

Scope in JavaScript defines what variables you have access to. There are two kinds of scope – global and local scope.

A local scope can be function scope and block scope.

What is the 'this' keyword and how is it used?

  • this always refers to an object.
  • this refers to an object which calls the function it contains.
  • In the global context this refers to either window object or is undefined if the ‘strict mode’ is used.

What Are JavaScript Programming Paradigms?

JavaScript is a multi-paradigm language, supporting imperative/procedural programming along with OOP (Object-Oriented Programming) and functional programming. JavaScript supports OOP with prototypal inheritance.

  • Prototypal inheritance (also: prototypes, OLOO).
  • Functional programming (also: immutability, pure functions, function composing, cursing, closures, first-class functions, lambdas).

Functional Programming

Functional Programming is a form of programming in which you can pass functions as parameters to other functions and also return them as values. In functional programming, we think and code in terms of functions.

Object-Oriented Programming

The basic idea of OOP is that we use objects to model real-world things that we want to represent inside our programs, and/or provide a simple way to access functionality that would otherwise be hard or impossible to make use of.

Objects can contain related data and code, which represent information about the thing you are trying to model, and the functionality or behaviour that you want it to have. Object data (and often, functions too) can be stored neatly (the official word is encapsulated) inside an object package (which can be given a specific name to refer to, which is sometimes called a namespace), making it easy to structure and access; objects are also commonly used as data stores that can be easily sent across the network.

Higher-order functions

A Higher-Order function is a function that receives a function as an argument or returns the function as output.

const double = n => n * 2

[1, 2, 3, 4].map(double) // [ 2, 4, 6, 8 ]

What is Object literal syntax?

Object literal is a comma-separated list of name-value pairs wrapped in curly braces.

Object literals encapsulate data, enclosing it in a tidy package.

Write Class example in JavaScript

class Person {
  constructor(name, surname) {
    this.name = name;
    this.surname = surname;
  }
  get fullName() {
    return this.name + " " + this.surname;
  }
  set fullName(name) {
    var words = name.toString().split(" ");
    this.name = words[0];
    this.surname = words[1];
  }
  sayHello() {
    console.log(`My name is ${this.name} ${this.surname}`);
  }
}

Determine what would be logged out to the console.

// What would be logged out to the console?
var num = 50;

function logNumber() {
    console.log(num);
    var num = 100;
}

logNumber();
// Undefined
// When this function is ran in what order the four numbers will be logged out?
function logNumbers() {
  console.log(1);
  setTimeout(function(){console.log(2)}, 1000);
  setTimeout(function(){console.log(3)}, 0);
  console.log(4);
}

logNumbers();
// 1 4 3 2
// Determine what would be logged out to the console.
(() => {
  const x = y = 100;
})();

console.log('y: ', y);
console.log('x: ', x);
// y: 100
// x is not defined

Write function that checks if the number is prime

A prime number (or a prime) is a natural number greater than 1 that has no positive divisors other than 1 and itself

const isPrime = num => {
  for(let i = 2; i < num; i++)
    if(num % i === 0) return false;
  return num > 1;
}

isPrime(5) //true
isPrime(6) //false

FizzBuzz

Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and the multiples of five print “Buzz”. For numbers that are multiples of both three and five print “FizzBuzz”.

// Solution 1
for (let i = 1; i <= 100; i++) {
  const f = i % 3 == 0;
  const b = i % 5 == 0;
  console.log(f ? (b ? "FizzBuzz" : "Fizz") : b ? "Buzz" : i);
}

// Solution 2
for (let i = 1; i <= 100; i++) {
  let res = "";
  if (i % 3 === 0) {
    res += "Fizz";
  }
  if (i % 5 === 0) {
    res += "Buzz";
  }
  console.log(res || i);
}

Revers string

Write a function that reverts string

// Solution 1
function revert(string) {
  const _reverted = string.split("").reverse().join("");
  return _reverted;
}
console.log(revert("Lukasz")); //zsakuL

// Solution 2
function revertTwo(string) {
  let _reverted = "";
  for (let i = string.length - 1; i >= 0; i--) _reverted += string[i];
  return _reverted;
}

console.log(revertTwo("Interview")); //weivretnI

Useful link and resources

If you liked this article, please share it on Twitter.