Javascript Interview Questions

Javascript Interview Questions

by John Vincent


Posted on February 23, 2017



Let’s describe some of the potential JavaScript interview questions.

My references

There are many excellent references. Let’s list a few:

tech-interview-handbook; Javascript Questions

tech-interview-handbook; Javascript utilities

Other

37 Questions

51 Questions

100 Advanced Questions

85 Questions

Let vs Var

  • var is hoisted, let is not
  • var is function scope, let is block scope
  • let is es2015, will not work on older browsers

== vs ===

Comparison operators.

== compares the value

=== compares the value and the type.

let vs const

const, cannot reassign, can modify if an object.

let, can change

undefined vs null

They both represent an empty value.

When define a variable but do not assign a value, the variable is undefined

null is expressly set.

typeof(undefined) => undefined

typeof(null) => object

Use of arrow function

Example

const profile = {
  first: '',
  last: '',
  setName: function(name) {
    let splitName = function(n) {
      let nameArray = n.split(' ');
      this.first = nameArray[0];	// same as window.first
      this.last = nameArray[1];
    }
    splitName(name);
  }
}

profile.setName('bill blunt');
console.log('first ', first); // => 'bill'

Arrow function does not have it’s own this

const profile = {
  first: '',
  last: '',
  setName: function(name) {
    let splitName = (n) => {    // 'this' refers to 'setName', which is profile object
      let nameArray = n.split(' ');
      this.first = nameArray[0];
      this.last = nameArray[1];
    }
    splitName(name);
  }
}

profile.setName('bill blunt');
console.log('first ', profile.first);

Example

var x = function() {
  this.val = 1;
  setTimeout(function() {
    this.val++;
    console.log(this.val);		// NaN
  }, 1);
};

var xx = new x();

as this refers to the function in setTimeout

Use that

var x = function() {
  var that = this;
  this.val = 1;
  setTimeout(function() {
    that.val++;
    console.log(that.val);
  }, 1);
};

var xx = new x();

or, using Arrow functions

var x = function() {
  this.val = 1;
  setTimeout(() => {
    this.val++;
    console.log(this.val);
  }, 1);
};

var xx = new x();
var x = function() {
  console.log(arguments[0]);
};
x(1,2,3);		// 1

var x = () => {
  console.log(arguments[0]);
};
x(1,2,3);		// {}

var x = (...n) => {
  console.log(n[0]);
};
x(1,2,3);		// 1

Prototypal Inheritance

Every object has a property called prototype.

The Object.prototype is on the top of the prototype inheritance chain. All objects inherit from Object.prototype.

The prototype property allows addition of new properties and methods to object constructors.

function Person(first, last, age, eyecolor) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
  this.eyeColor = eyecolor;
}

Person.prototype.nationality = "English";

Person.prototype.name = function() {
  return this.firstName + " " + this.lastName;
};

Class Inheritance vs Prototypal Inheritance?

Class Inheritance

A class is like a blueprint — a description of the object to be created. Classes inherit from classes and create subclass relationships: hierarchical class taxonomies.

Instances are typically instantiated via constructor functions with the new keyword.

Prototypal Inheritance

A prototype is a working object instance. Objects inherit directly from other objects.

Instances are typically instantiated via factory functions, object literals, or Object.create()

function declaration vs function expression

console.log(funcD());
console.log(funcE());		// not defined

function funcD() {			// cannot pass to another function
	console.log('function declaration');
}

let funcE = function() {	// can pass to another function
	console.log('function expression');
}

What are Promises

Use a Promise if need to make an async call that has to wait for the response to continue.

A promise is an object which can be returned synchronously from an asynchronous function. It will be in one of 3 possible states:

  • Fulfilled: onFulfilled() will be called (e.g., resolve() was called)
  • Rejected: onRejected() will be called (e.g., reject() was called)
  • Pending: not yet fulfilled or rejected

Example

const wait = time => new Promise((resolve) => setTimeout(resolve, time));

wait(3000).then(() => console.log('Hello!')); // 'Hello!'
  • Promise.reject() returns a rejected promise.
  • Promise.resolve() returns a resolved promise.
  • Promise.race() takes an array (or any iterable) and returns a promise that resolves with the value of the first resolved promise in the iterable, or rejects with the reason of the first promise that rejects.
  • Promise.all() takes an array (or any iterable) and returns a promise that resolves when all of the promises in the iterable argument have resolved, or rejects with the reason of the first passed promise that rejects.

Promise Chaining

.then() always returns a new promise, thus can chain.

fetch(url)
  .then(process)
  .then(save)
  .catch(handleErrors)
;

setTimeout()

setTimeout(function() {
  console.log('a');
}, 0);
console.log('b');
console.log('c');

b
c
a

as b and c are on the stack and will be excuted first, then the asynchronous will get ot the event loop and be executed.

What is a closure and how to you use it

When a function returns another function, the returning function will have access to the variables in the outer function scope.

let obj = function() {
  let i = 0;			// environment

  return {
    setI(k) {
      i = k;
    },
    getI() {
      return i;
    }
  }
}

let x = obj();
x.setI(2);
console.log(x.getI());		// 2

Closures are frequently used in JavaScript for

  • object data privacy
  • event handlers and callback functions, and in partial applications, currying, and other functional programming patterns.

Function Composition

Function composition is the process of combining two or more functions to produce a new function. Composing functions together is like snapping together a series of pipes for our data to flow through.

const toSlug = input => encodeURIComponent(
  input.split(' ')
    .map(str => str.toLowerCase())
    .join('-')
);

Functional Programming

Functional programming is a programming paradigm, a style of building the structure and elements of computer programs, that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.

Functional programming is the process of building software by composing pure functions, avoiding shared state, mutable data, and side-effects.

Functional programming is declarative rather than imperative, and application state flows through pure functions. Contrast with object oriented programming, where application state is usually shared and colocated with methods in objects.

idempotent

The output value of a function depends only on the arguments that are passed to the function, so calling a function f twice with the same value for an argument x produces the same result f(x) each time.

Pure Function

A pure function is a function that has the following properties:

Given the same inputs:

  • always returns the same output, and
  • has no side-effects

Higher Order Function

A higher order function is any function which takes a function as an argument, returns a function, or both. Higher order functions are often used to:

  • Abstract or isolate actions, effects, or async flow control using callback functions, promises, monads, etc…
  • Create utilities which can act on a wide variety of data types
  • Partially apply a function to its arguments or create a curried function for the purpose of reuse or function composition
  • Take a list of functions and return some composition of those input functions

Declarative vs Imperative

Functional programming is a declarative paradigm, meaning that the program logic is expressed without explicitly describing the flow control.

Imperative programs spend lines of code describing the specific steps used to achieve the desired results — the flow control: How to do things.

Imperative example

const doubleMap = numbers => {
  const doubled = [];
  for (let i = 0; i < numbers.length; i++) {
    doubled.push(numbers[i] * 2);
  }
  return doubled;
};

console.log(doubleMap([2, 3, 4])); // [4, 6, 8]

Declarative programs abstract the flow control process, and instead spend lines of code describing the data flow: What to do. The how gets abstracted away.

Declarative example

const doubleMap = numbers => numbers.map(n => n * 2);

console.log(doubleMap([2, 3, 4])); // [4, 6, 8]

Other

console.log(2 + '2');		// 22
console.log(2 - '2');		// 0
console.log(5 < 6 < 7);		// true
console.log(7 > 6 > 5);		// false

as

console.log(true < 7);		// 1 < 7 => true
console.log(true > 5);		// 1 not greater than 5 => false
let a = () => arguments;	// arguments do not bind to the arrow function
console.log(a('hi'));		// arguments is not defined

let a = function(arguments) {
  return arguments;
}
console.log(a('hi'));		// hi

let a = (...n) => n;
console.log(a('hi'));		// ['hi']

Make an array of unique numbers

let nums = [1, 2, 2, 3];
const jv = new Set(nums);   // returns a set of unique numbers
console.log([...jv]);	// array

Object.freeze() vs Object.seal()

.freeze() prevents any changes to the object.

let profile = {
  name: 'bill'
};
profile.age = 3;
console.log(profile);		// { name: 'bill', age: 3 }
let profile = {
  name: 'bill'
};
Object.freeze(profile);
profile.name = 'fred';
profile.age = 3;
console.log(profile);		// { name: 'bill' }

.seal() allows changes to already existing properties and prevents addition of new properties.

let profile = {
  name: 'bill'
};
Object.seal(profile);
profile.name = 'fred';
profile.age = 3;
console.log(profile);		// { name: 'bill' }

Object.defineProperty()

Add properties to an object.

var profile = {
  name: 'bill'
};
Object.defineProperty(profile, 'age', {
  value:3,
  writable: false,
  enumerable: true,
});

profile.name = 'fred';	// changed
profile.age = 4;		// not changed
console.log(profile);	// { name: 'fred', age: 3 }

Math.max()

console.log(Math.max());		// -Infinity

Array myReduce()

Array.prototype.myReduce = function(fn, init) {
  let sum = init;
  this.forEach(item => {
    sum = fn(item, sum);
  });
  return sum;
}

const total = [1,2,3].myReduce((c,v) => c + v, 0);
console.log('total ', total);		// 6

Reverse a string

function reverse(str) {
	if (!str) {
		return str;
	}
	return str.split('').reduce((accumulator, chr) => {
		return chr + accumulator;
	}, '');
}

const str = 'abcd';
console.log(reverse(str));	// 'dcba'

or

function reverse(str) {
	if (!str) {
		return str;
	}
	return str.split('').reduce((c, v) => v + c, '');
}

Create Promise

let myPromise = function(fn) {
  return new Promise(function(resolve, reject) {
    if (fn)
      resolve('good');
    else
      reject('error');
  });
}

function fn() {
  console.log('fn');
}

myPromise(fn)
.then(value => { 
  console.log('(1) value ', value);
})
.catch(err => { 
  console.log('(1) err ', err);
});

myPromise(null)
.then(value => { 
  console.log('(2) value ', value);
})
.catch(err => { 
  console.log('(2) err ', err);
});

f(1)(2)(3)

function f(a, b, c) {
  if (! a) return 0;
  if (a && b && c) return (a + b) * c;

  return function(b) {
    return function(c) {
      return (a + b) * c;
    }
  }
}

console.log('f() ', f());			// 0
console.log('f(null) ', f(null));		// 0

console.log('f(3, 2, 4) ', f(3, 2, 4));	// 20
console.log('f(1)(2)(3) ', f(1)(2)(3));	// 9
console.log('f(2)(2)(1) ', f(2)(2)(1));	// 4

Add to start of Array

var myArray = ['a', 'b', 'c', 'd'];
myArray.unshift('1','2');
console.log(myArray);	// ["1", "2", "a", "b", "c", "d"]

Add to end of Array

var myArray = ['a', 'b', 'c', 'd'];
myArray.push('e', 'f');
console.log(myArray);	// ["a", "b", "c", "d", "e", "f"]

How to create a private variable

function secretVariable() {
  let secret = 'a secret';
  return function() {
    return secret;
  }
}

let obj = secretVariable();
console.log(obj);		// the function

console.log(obj());		// 'a secret'
function rectangle(x, y) {
  this.x = x;
  this.y = y;
  return function() {
    return x * y;
  }
}

const a = rectangle(3,2);
console.log('a ', a);
console.log('a() ', a());
function outer(param) {
  let body = param;
  return {
    getBody() {
      return body;
    }
  }
}

let x = outer('some value');
console.log(x.getBody());	// some value

console.log('test 1 ', x.body);	// undefined
function rectangle(width, height) {
  this.width = width;
  this.height = height;
  return {
    area: function() {
      return width * height;
    },
    width: function() {
      return width;
    },
    height: function() {
      return height;
    }
  }
}

const a = rectangle(3,2);
console.log('a ', a);
console.log('a.area() ', a.area());
console.log('a.width() ', a.width());
console.log('a.height() ', a.height());

What is the output

var num = 4;
function outer() {
  var num = 2;
  function inner() {
    num++;
    var num = 3;
    console.log(num);		// 3
  }
  inner();
}
outer();

What is the output

console.log(typeof typeof 1);		// string

as

console.log(typeof 1);		// 'number'

What is the output

var hero = {
  _name: 'bill',
  getName: function() {
    return this._name;
  }
};
var name = hero.getName;

console.log('name() ', name());		// undefined
console.log('hero.getName() ', hero.getName());	// 'bill'

var test = hero.getName.bind(hero);
console.log('test() ', test());		// 'bill'

Character Map

for (const char of str) {
	// charMap[char] = charMap[char] + 1 || 1;
	if (charMap[char]) {
		charMap[char]++;
	} else {
		charMap[char] = 1;
	}
}

or

for (const char of str) {
	charMap[char] = charMap[char] + 1 || 1;
}

MaxChars

function maxChar(str) {
	const charMap = {};

	let hv = 0;
	let hk = '';
	for (const char of str) {
		// charMap[char] = charMap[char] + 1 || 1;
		if (charMap[char]) {
			charMap[char]++;
		} else {
			charMap[char] = 1;
		}
		// if (charMap[char] > hv) {
		// 	hk = char;
		// 	hv = charMap[char];
		// }
	}

	for (const char in charMap) {
		console.log('char ', char);
		if (charMap[char] > hv) {
			hk = char;
			hv = charMap[char];
		}
	}
	return hk;
}

Chunked Array

function chunk(array, size) {
	const result = [];
	for (const element of array) {
		const last = result[result.length - 1];
		if (!last || last.length === size) {
			result.push([element]);
		} else {
			last.push(element);
		}
	}
	return result;
}

or

function chunk(array, size) {
	const result = [];
	for (let i = 0; i < array.length; ) {
		result.push(array.slice(i, i + size));
		i += size;
	}
	return result;
}

or

function chunk(array, size) {
	const result = [];
	let index = 0;
	while (index < array.length) {
		result.push(array.slice(index, index + size));
		index += size;
	}
	return result;
}

Anagrams

function clean(str) {
	const letters = /[^A-Za-z]/g;
	return str.replace(letters, '').toLowerCase().split('').sort().join('');
}

function anagrams(stringA, stringB) {
	return clean(stringA) === clean(stringB);
}

or

function makeMap(str) {
	const letters = /[^A-Za-z]/g;
	const charMap = {};
	for (const char of str.replace(letters, '').toLowerCase()) {
		charMap[char] = charMap[char] + 1 || 1;
	}
	return charMap;
}

function anagrams(stringA, stringB) {
	const map1 = makeMap(stringA);
	const map2 = makeMap(stringB);
	if (Object.keys(map1).length !== Object.keys(map2).length) {
		return false;
	}

	for (const char in map1) {
		if (map1[char] !== map2[char]) {
			return false;
		}
	}
	return true;
}

Capitalize

function capitalize(str) {
	let result = '';
	for (let i = 0; i < str.length; i++) {
		if (i === 0 || str[i - 1] === ' ') {
			result += str[i].toUpperCase();
		} else {
			result += str[i];
		}
	}
	return result;
}

or

function capitalize(str) {
	const result = [];
	for (const word of str.split(' ')) {
		result.push(`${word[0].toUpperCase()}${word.slice(1)}`);
	}
	return result.join(' ');
}

Printing Steps

Using recursion

function steps(n, row = 0, stair = '') {
	if (row === n) {
		return;
	}
	if (n === stair.length) {
		console.log(stair);
		steps(n, row + 1);
		return;
	}

	if (stair.length <= row) {
		stair += '#';
	} else {
		stair += ' ';
	}
	steps(n, row, stair);
}

or, more simply

function nextChar(str = '', i, m, n) {
	if (i <= n) {
		return nextChar(str + (i <= m ? '#' : ' '), i + 1, m, n);
	}
	return str;
}

function steps(n) {
	for (let m = 1; m <= n; m++) {
		const result = nextChar('', 1, m, n);
		console.log(result);
	}
}

A simple solution

function steps(n) {
	for (let row = 1; row <= n; row++) {
		let result = '';
		for (let column = 0; column < n; column++) {
			if (column < row) {
				result += '#';
			} else {
				result += ' ';
			}
		}
		console.log(result);
	}
}

or

function makeChars(m, n) {
	let result = '';
	for (let i = 0; i < n; i++) {
		if (i < m) {
			result += '#';
		} else {
			result += ' ';
		}
	}
	return result;
}

function steps(n) {
	for (let m = 1; m <= n; m++) {
		console.log(makeChars(m, n));
	}
}
function makeChars(char, n) {
	let result = '';
	for (let i = 0; i < n; i++) {
		result += char;
	}
	return result;
}

function steps(n) {
	for (let i = 1; i <= n; i++) {
		console.log(`${makeChars('#', i)}${makeChars(' ', n - i)}`);
	}
}

Using String.repeat()

function steps(n) {
	for (let i = 1; i <= n; i++) {
		console.log(`${'#'.repeat(i)}${' '.repeat(n - i)}`);
	}
}

Pyramid

function pyramid(n) {
	const columns = n * 2 - 1;
	for (let row = 1; row <= n; row++) {
		const hashes = row * 2 - 1;
		const outer = (columns - hashes) / 2;
		console.log(`${' '.repeat(outer)}${'#'.repeat(hashes)}${' '.repeat(outer)}`);
	}
}

Vowels

Iterative

function vowels(str) {
	let count = 0;
	const checker = ['a', 'e', 'i', 'o', 'u'];
	for (const char of str.toLowerCase()) {
		if (checker.includes(char)) {
			count++;
		}
	}
	return count;
}

Regexp

function vowels(str) {
	const matches = str.match(/[aeiou]/gi);
	return matches ? matches.length : 0;
}

or

function vowels(str) {
	const letters = /[^AEIOUaeiou]/g;
	return str.replace(letters, '').length;
}

Spiral Matrix

function matrix(n) {
	const array = [];
	for (let i = 0; i < n; i++) {
		array.push([]);
	}

	let rowStart = 0;
	let rowEnd = n - 1;
	let colStart = 0;
	let colEnd = n - 1;
	let counter = 1;
	let dir = 1;
	while (counter <= n * n) {
		if (dir === 1) { // top row
			for (let idx = colStart; idx <= colEnd; idx++) {
				array[rowStart][idx] = counter++;
			}
			rowStart++;
			dir = 2;
		} else if (dir === 2) { // right column
			for (let idx = rowStart; idx <= rowEnd; idx++) {
				array[idx][colEnd] = counter++;
			}
			colEnd--;
			dir = 3;
		} else if (dir === 3) { // bottom row
			for (let idx = colEnd; idx >= colStart; idx--) {
				array[rowEnd][idx] = counter++;
			}
			rowEnd--;
			dir = 4;
		} else if (dir === 4) { // left column
			for (let idx = rowEnd; idx >= rowStart; idx--) {
				array[idx][colStart] = counter++;
			}
			colStart++;
			dir = 1;
		}
	}
	return array;
}