Skip to main content

Web Development Interview Preparation

· 5 min read
(function(){
// Isolate Code
})();

A immediately invoked function expression (IIFE) is a JavaScript function that runs as soon as it is defined. It is used to create an anonymous closure that ensures that the variables within are not accessible or colliding with other variables outside of the scope.

It is possible to pass arguments to the IIFE.

(function(name){
// Isolate Code
})("John Doe");

Immutablility

Similar to Java, if you declare a variable with const, you cannot change the value of the variable. But, you can still change the reference of the object that the variable is pointing to. This is most obvious when the variable is an array or an object. One way to deal with this is to keep to the use of functions like map, filter, and reduce. These operations will not change the original content of the variable.

Reduce

Reduce is used to succinctly combine multiple values in each iteration into a single value.

const numbers = [1, 2, 3, 4, 5];
numbers.reduce((accumulator, currentValue) => {
return accumulator + currentValue;
}, 0);

use strict

As compared to "sloppy" code, "strict" mode is more restrictive. For example:

"use strict";
myVar = "Hello World"; // Error: myVar is not defined
  1. forgetting to declare the variable properly with const or let will result in an error.
  2. invalid assignment to reserved variable names such as undefined or NaN will result in an error.
  3. modifying properties that cannot be modified will result in an error.
  4. etc

Wrapping the code in use strict via an IFFE will ensure that strict mode does not impact other scripts that are combined with the current script. This is to avoid declaring strict mode globally and perhaps affect code that you imported.

Promises

Callbacks are functions passed as arguments, mostly used asynchronously. Promises guarantee that a function will be called only once, and that the function will be called in the future.

new Promise(function(resolve, reject) {
// Do something
if (condition) {
resolve("Success!");
} else {
reject("Failure!");
}
}).then(function(successMessage) {
// Do something
// successMessage is the value that was passed to resolve
}).catch(function(failureMessage) {
// Do something
});

Virtual DOM

The traditional DOM is a tree representation of a webpage. Updating the DOM is expensive and can cause performance issues. The virtual DOM is a representation of the DOM that is not updated until the virtual DOM is compared to the actual DOM.

Hoisting

Move all declarations to the top of the current scope. Assignments are not hoisted. One way to deal with it is to use IFFE. Variables inside the for loop are also hoisted if declare with var.

// Declarations are hoisted
var x;
// Assignments are not hoisted
x = 1;
// Declare and assign
var x = 1;
// Functions are hoisted
foo() // OK
function foo() {
// Do something
}
// Assigned functions are not hoisted
bar() // Error
const bar = function() {
// Do something
}

Ems and Rems

They are more flexible, but based on the default font size of 16px(could be different). Rems are relative to the default font size.

div {
font-size: 2rem; // 2rem = 16px * 2
}

Ems are relative to the container.

h1 {
font-size: 2em; // 2em = 16px * 2 / 16px
}

Pseudo Classes and Pseudo Elements

Pseudo classes are used to select existing elements based on a state or a property. Pseudo classes: :hover, :focus, :first-child Pseudo elements: ::before, ::after, ::first-letter

Array assignment

Javascript arrays have undefined as their default value. When assigning a value to an array at a out-of-bound index, the array will pad undefined in the middle such that the length of the array is until the last added item that is not undefined.

var arr = [1, 2, 3];
arr[4] = 4;
arr; // [1, 2, 3, undefined, 4]

Sharing data between tabs

localStorage, cookie, indexedDB can be used to share data across different tabs. For sessionStorage, opening a page in a new tab or window will cause a new session to be initiated.

Async and Await

Event loop:

  • call stack
  • task queue
  • microtask queue
  • macrotask queue
console.log('Sync 1');

setTimeout(() => { // queue to macrotask queue
console.log('Timeout 2');
}, 0);

Promise.resolve().then( // queue to microtask queue
() => {
console.log('Promise 3');
}
);

console.log('Sync 4');
// Result
// Sync 1
// Sync 4
// Promise 3
// Timeout 2

The creation of the promise is still happening in the main thread, it's the resolving of the value that is happening in the microtask queue.

const codeBlocker = () => {

// Blocking
let i = 0;
while(i < 1000000000) { i++;}
return '🐷 billion loops done';

// Async blocking
return new Promise((resolve, reject) => {
let i = 0;
while(i < 1000000000) { i++;}
resolve('🐷 billion loops done');
})

// Non-blocking
return Promise.resolve().then(v => {
let i = 0;
while(i < 1000000000) { i++; }
return '🐷 billion loops done';
})
}
console.log("1");
setTimeout(function () {
console.log("timeout");
}, 0)
new Promise(function (resolve) {
console.log("in promise");
resolve()
}).then(function () {
console.log("chained then")
})

console.log("2");

// Result
// 1
// in promise
// 2
// chained then
// timeout