TODO: review and update general docs
- You Don't Know JS (free)
- Eloquent JavaScript (free)
- awesome-interview-questions
- Front-end-Developer-Interview-Questions
- 21 Essential JavaScript Interview Questions
- 37 Essential JavaScript Interview Questions (TopTal)
- JS: Basics and Tricky Questions
-
general:
- event bubbling / event capturing (trickling)
- mutable vs immutable
-
JS core:
- data types
- scope / lexical environment
- closure
- hoisting
- prototypes
- this
- new O()
- call, apply, bind
- obj.func()
- func()
- () =>
- async
- single threaded
- JS Engine / JS execution context
- heal, call stack
- web api (timers, XHR, DOM etc.)
- event loop, callback queue
- promise, micro-tasks
- functional programming
- Higher order functions
- Currying
- First Order functions
-
function, class
- class vs function
- pure function
- first-class functions
- IIFE / module pattern
- static
-
DOM manipulations
Q: Two main programming paradigms in JavaScript, its difference
A:
- OOP (SOLID, classes, prototypal inheritance)
- pros: easy to understand, use imperative over declarative style, reads more straight-forward
- cons: depends on shared state, may lead to race condition
- Functional programming (first class functions, closures)
- pros: no shared state or side-effects leads to no bugs related to race conditions, easy to recompose functions for more reusable code. Things don't compete for shared resources. Object composition over class inheritance.
- cons: code may lock less concrete, more abstractly specified, may be confusing for those who come from OOP
Q: What is functional programming?
A: It is a paradigm to provide programs by composing functions and avoid shared state & mutable data. It is one of two essential concepts in JavaScript (OOP as another one).
Features: pure functions, no side effects, function composition, first-class func, HOF, func as arguments/values, Immutability
Other functional langs: Lisp, Haskell, Erlang, Closure, F Sharp.
Q: Classical vs prototypal inheritance?
A: Class Inheritance: instances inherit from classes, and create tight coupling, hierarchical relationships.
Instances are typically instantiated via constructor functions with the `new` keyword.
Class inheritance may or may not use the `class` keyword from ES6.
Prototypal Inheritance: instances inherit directly from other objects. Instances are typically instantiated via
factory functions or `Object.create()`. Instances may be composed from many different objects, allowing for easy
selective inheritance. Concatenative inheritance, prototype delegation, functional inheritance, object composition. Delegation (prototype chain), Concatenative (mixins, Object.assign()), Functional (create closure for private state/encapsulation)
Q: Object composition over Class inheritance'
A:
<https://www.youtube.com/watch?v=wfMtDGfHWpA&feature=emb_title>
It means that code reuse should be achieved by assembling smaller units of functionality into new objects instead
of inheriting from classes and creating object taxonomies. In other words, use can-do, has-a, or uses-a
relationships instead of is-a relationships.
Avoid class hierarchies, avoid brittle base class problem (changes in a base class may lead to problem in child
classes), avoid tight coupling, avoid forced is-a relationship, avoid gorilla-banana problem (get entire jungle
but not simple banana), makes code more flexible
Q: ES6(2015)+ features <https://github.com/lukehoban/es6features>
A:
ES6 (2015):
() =>
class, extends
enhanced object literals: (Shorthand for ‘handler: handler’, __proto__, {methods(){}}, super calls, dynamic props)
template strings + string interpolation: `${prop} \nsdf`
destructuring: var [a, , b, c=5] = [1,2,3]; var { op: a, lhs: { op: b }, rhs: c } = getASTNode(); g({name: 5})
default + rest + spread: function f(x, y=12); function f(x, ...y); f(...[1,2,3])
let + const
iterators + for..of
generators
unicode
modules (import, export)
module loaders
map + set + weakmap + weakset
promises
math + number + string + array + object APIs
proxies
symbols
subclassable built-ins
binary and octal literals
reflect api
tail calls
ES7 (2016):
Array.prototype.includes()
Exponentiation operator **
ES8 (2017):
async, await
shared memory and atomics
Object.values/Object.entries
String padding // myString.padStart(2); // or padEnd
Object.getOwnPropertyDescriptors()
Trailing commas in function parameter lists and calls // function test(a,b,c, ) // notice the comma after c
ES9 (2018):
Asynchronous Iteration
Rest/Spread Properties
RegExp named capture groups
RegExp Unicode Property Escapes
RegExp Lookbehind Assertions
s (dotAll) flag for regular expressions
Promise.prototype.finally()
Template Literal Revision
ES10 (2019):
Array.flat: [[1,2],3]).flat() // [1,2,3]
Array.flatMap: equivalent of map().flat()
Object.fromEntries: reverse operation from Object.entries
String.trimStart() & String.trimEnd(): Remove extra spaces in a string
Optional Catch binding: remove the need to add a param to the catch (Now you can do } catch {instead of } catch(e) {
Function.toString has been revisited to have a consistent behavior
Symbol Description
BigInt — arbitrary large numbers (Thanks @redeyes2015 for correction)
Improvement on Unicode support for JSON.stringify()
Array.sort now retains order if keys are equal
Make JavaScript a superset of JSON
Q: 'use strict'
A: Used to enforce stricter parsing and error handling of a js code at a runtime. Errors that would normally be ignored would generate errors, or throw exceptions
Q: Data Types
// Primitives, value type
undefined: typeof instance === 'undefined';
Boolean: typeof instance === 'boolean';
Number: typeof instance === 'number'; // NaN
String: typeof instance === 'string';
BigInt: typeof instance === 'bigint';
Symbol: typeof instance === 'symbol';
Null: typeof instance === 'object';
// Objects, ref type
Object: typeof instance === 'object'; // structural type
// data structures
new Object(),
new Array(),
new Map(),
new Set(),
new WeakMap(),
new WeakSet(),
new Date();
Function: typeof instance === 'function'; // derived from Object
}
Q: Comparison, Castings, find Data Type
// == vs ===
// cast string to number (if wrong input, might return NaN)
'10' * 1 + // the fastest
''; // fast
Number('10');
parseInt('10');
parseFloat('10');
Math.floor('10');
// cast to string
String(x);
x.toString();
JSON.stringify(a);
// cast to object
JSON.parse();
5 + null; // returns 5 because null is converted to 0
'5' + null; // returns "5null" because null is converted to "null"
'5' + 2; // returns "52" because 2 is converted to "2"
'5' - 2; // returns 3 because "5" is converted to 5
'5' * '2'; // returns 10 because "5" and "2" are converted to 5 and 2
true + 0; // 1
true + 'xz'; // 'truexz'
true + true; // 2
true + false; // 1
// Check type
typeof 'John'; // Returns "string"
typeof 3.14; // Returns "number"
typeof NaN; // Returns "number"
typeof false; // Returns "boolean"
typeof [1, 2, 3, 4]; // Returns "object"
typeof { name: 'John', age: 34 }; // Returns "object"
typeof new Date(); // Returns "object"
typeof function() {}; // Returns "function"
typeof myCar; // Returns "undefined" *
typeof null; // Returns "object"
1 < 2 < 3; // true (true < 3)
3 > 2 > 1; // false (true > 1)
(1 == 1) == 1; // true
(1 === 1) === 1; // false
0.1 + 0.2 == 0.3; // false (could be true, floating value issue)
Q: Monolithic vs Microservice architecture?
Monolithic:
App written as one unit of code, whose components designed to work together, sharing same memory and resources.
Pros: cover large number of cross-cutting concerns, shared memory process are faster then inter-process communication (IPC)
Cons: tightly coupled, difficult to isolate services (scalability, maintainability problem), harder to understand due to deps, side-effects
Microservice:
App consists of several smaller independent apps that run in their own memory and space and scale independently from
each other across potentially many separate machines
Pros: better organized, separation of concern, easy to recompose, reconfigure, scale only what you need. Perform
and scale better in long run.
Cons: shared middleware, more devOps job needed, higher initial cost
}
// WHAT OUTPUT & WHY ?
// NFE (Named Function Expression)
var foo = function bar(){ return 12; };
typeof bar(); // ReferenceError: bar is not defined,
// to fix - check type of assigned variable instead, or use function declaration and check func type
// WHAT OUTPUT & WHY ?
var z = 1, y = z = typeof y; // here associativity of the assignment is Right to Left
console.log(y); // undefined
// WHAT OUTPUT & WHY ?
var trees = ["redwood","bay","cedar","oak"];
delete trees[2];
console.log(trees.length); // 4
console.log(trees); // ["redwood","bay", empty ,"oak"]
console.log(trees[2]); // undefined
// WHAT OUTPUT & WHY ?
var output = (function(x){
delete x; // delete won't work here because it deletes only obj props, but not variables (neither local nor global)
return x;
})(0); // 0
// WHAT OUTPUT & WHY ?
var x = { foo : 1};
var output = (function(){
delete x.foo;
return x.foo;
})();
console.log(output); // undefined
// WHAT OUTPUT & WHY ?
console.log(
(function f(n) {
return n > 1 ? n * f(n - 1) : n;
})(10)
); // 10!
// WHAT OUTPUT & WHY ?
var foo = new Object();
var bar = new Object();
var map = new Object();
map[foo] = 'foo'; // map = {[object Object]: 'foo'}
map[bar] = 'bar'; // map = {[object Object]: 'bar'}
console.log(map[foo]); // bar
// WHAT OUTPUT & WHY ?
var a = {},
b = { key: 'b' },
c = { key: 'c' };
a[b] = 123; // a = {[object Object]: 123}
a[c] = 456; // a = {[object Object]: 456}
console.log(a[b]); // {[object Object]: 456}
// WHAT OUTPUT & WHY ?
var arr1 = 'john'.split('');
var arr2 = arr1.reverse();
var arr3 = 'jones'.split('');
arr2.push(arr3);
console.log('array 1: length=' + arr1.length + ' last=' + arr1.slice(-1)); // "array 1: length=5 last=j,o,n,e,s"
console.log('array 2: length=' + arr2.length + ' last=' + arr2.slice(-1)); // "array 2: length=5 last=j,o,n,e,s"
// WHAT OUTPUT & WHY ?
// RECURSION
// how to fix?
let counter = 0;
(function doJob() {
if (counter > 3) return 'done!';
counter++;
doJob(); // fix: return doJob()
}())
// undefined, because only last function in a stack returned value, rest just finished
// bubble up returned value, return recursive call but not just call
{ // reverse string
'0123456789'.split('').reverse().join('');
}
{ // check if input string is palindrome
function isPalindrome(str) {
var strAlphaNumeric = str.replace(/\W/g, '').toLowerCase();
return strAlphaNumeric == strAlphaNumeric.split('').reverse().join('');
}
console.log(isPalindrome('level')); // logs 'true'
console.log(isPalindrome('levels')); // logs 'false'
console.log(isPalindrome('A car, a man, a maraca')); // logs 'true'
}
{ // how to empty an array?
array = [];
array.length = 0;
while(array.length > 0) { array.pop(); }
array.splice(0, array.length)
}
{ // how to remove duplicates from array?
// solution #1 (Fast #1)
[...new Set(arr)] // or Array.from(new Set(arr))
// solution #2 (Fast #2)
arr.filter((elem, index, self) => index === self.indexOf(elem));
// solution #3 (Fast #3) - slowest method when almost no dubs.
arr.reduce((uniqueArr, currentItem) =>
unique.includes(currentItem) ? uniqueArr : [...uniqueArr, currentItem], []);
// solution #4
arr => {
let unique = {};
arr.forEach(e => { if (!unique[e]) unique[e] = true; });
return Object.keys(unique); // be aware, it returns array of strings only.
}
// solution #5
arr => {
let unique = [];
for (el of arr) unique[el] = 0;
return [...Object.keys(unique)]
}
// solution #6
arr => {
let unique = [];
for (el of arr) { if (!unique.includes(el)) unique.push(el) };
return unique;
}
}
{ // find SUM of all array elements
[1, 2, 3, 4, 5, 6, 7, 8, 9].reduce((acc, current) => acc + current, 0);
}
{ // create func that will give such result (relates to Partial Applied function)
var addSix = createBase(6);
addSix(10); // returns 16 // closure
addSix(21); // returns 27 // closure
// solution #1
function createBase(baseNumber) {
return function(n) {
// we are referencing baseNumber here even though it was declared
// outside of this function. Closures allow us to do this in JavaScript
return baseNumber + n;
};
}
// solution #2 (using arrow fn)
const createBase = base => n => base + n; // second function is a closure.
}
{ // create func that will give such result
add(2, 5); // 7
add(2)(5); // 7 // closure
// solution
const add = (a, b) => (b ? a + b : b => a + b);
}
{ // how to copy/clone object || arr
// Shallow copy
(/*Object.assign - shallow copy*/) => {
let a = { x: { z: 1 }, y: 2 };
let b = Object.assign({}, a);
b.x.z = 0;
console.log(JSON.stringify(a)); // {"x":{"z":0},"y":2} - original also changed by ref
console.log(JSON.stringify(b)); // {"x":{"z":0},"y":2}
};
(/*... - shallow copy*/) => {
let a = { x: { z: 1 }, y: 2 };
let b = { ...a };
b.x.z = 0;
console.log(JSON.stringify(a)); // {"x":{"z":0},"y":2} - original also changed by ref
console.log(JSON.stringify(b)); // {"x":{"z":0},"y":2}
};
(/*Array.from(arr)*/) => {
let a = [{ x: 1, y: 2, z: 3 }];
let b = Array.from(a);
b[0].x = 0;
console.log(JSON.stringify(a)); // [{"x":0,"y":2,"z":3}] - original also changed by ref
console.log(JSON.stringify(b)); // [{"x":0,"y":2,"z":3}]
};
// Deep copy
(/*Solution 3: converting to JSON and back (be careful, source should be JSON safe)*/) => {
let a = [{ x: { z: 1 }, y: 2 }];
let b = JSON.parse(JSON.stringify(a));
b[0].x.z = 0;
console.log(JSON.stringify(a)); // [{"x":{"z":1},"y":2}]
console.log(JSON.stringify(b)); // [{"x":{"z":0},"y":2}] - only copy changed
};
(/*Solution 4: deep copy using iteration*/) => {
function iterationCopy(src) {
let target = {};
for (let prop in src) {
if (src.hasOwnProperty(prop)) {
target[prop] = src[prop];
}
}
return target;
}
const source = { a: 1, b: 2, c: 3 };
const target = iterationCopy(source);
console.log(target); // {a:1, b:2, c:3}
// Check if clones it and not changing it
source.a = 'a';
console.log(source.a); // 'a'
console.log(target.a); // 1
};
// shallow copy array
var newArr = arr.slice();
var newArr = [...arr];
var newArr = Array.from(arr);
// deep copy array
var newArr = JSON.parse(JSON.stringify(arr));
};
{ // pull data from public API and show it on FE
// TODO
};