Javascript

Source: https://medium.com/javascript-scene/10-interview-questions-every-javascript-developer-should-know-6fa6bdf5ad95

Key features

  • Interpreted language

  • No multithreading or multiprocess capabilities

  • Case-sensitive

Hoisting

Variables declared or initialized with the var keyword will have their declaration "moved" up to the top of the current scope.

null vs undefined

Two distinct values for nothing: null and undefined.

undefined

Means: value of the variable is not defined.

Global variable undefined whose value is "undefined"

typeof undefined is also "undefined"

undefined is not a constant or a keyword. undefined is a type with exactly one value: undefined.

Assigning a new value to it does not change the value of the type undefined.

8 Ways to get Undefined:

  • A declared variable without assigning any value to it.

  • Implicit returns of functions due to missing return statements.

  • return statements that do not explicitly return anything.

  • Lookups of non-existent properties in an object.

  • Function parameters that have not passed.

  • Anything that has been set to the value of undefined.

  • Any expression in the form of void(expression)

  • The value of the global variable undefined

null

Means empty or non-existent value. Used by programmers to indicate “no value”.

Primitive value. Not an object. You can assign null to any variable.

Sometimes people wrongly assume that it is an object, because typeof null returns "object".

null == undefined ref: history of typeof null

undefined vs not defined

If you try to use a variable that doesn't exist and has not been declared, then JavaScript will throw an error var name is not defined and the script will stop executing thereafter. But If you use typeof undeclared_variablethen it will return undefined.

declaration vs definition

var x is a declaration because you are not defining what value it holds yet, but you are declaring its existence and the need for memory allocation.

var x = 1 is both declaration and definition (also we can say we are doing initialisation).

Every variable declaration and function declaration brings to the top of its current scope in which it's declared then assignment happen in order this term is called hoisting.

A variable can be declared but not defined. When we try to access it, It will result undefined.

var x; // Declaration
typeof x === 'undefined'; // Will return true

A variable can be neither declared nor defined. When we try to reference such variable then the result will be not defined.

console.log(y);  // Output: ReferenceError: y is not defined

== vs ===

== will not check types and === will check whether both sides are of same type.

== converts to its convenient type to have both in same type and then do the comparison.

=== compares the types and values. Hence, if both sides are not same type, answer is always false.

Rule for implicit coercion:

  • If both operands are same type use ===

  • undefined == null

  • If one operands is string another is number, convert string to number

  • If one is boolean and another is non-boolean, convert boolean to number and then perform comparison

  • While comparing a string or number to an object, try to convert the object to a primitive type and then try to compare

Be careful while comparing objects, identifiers must reference the same objects or same array.

var a = {a: 1};
var b = {a: 1};
a == b //false
a === b //false

var c = a;
a == c//true
a === c //true

Special note: NaN, null and undefined will never === another type. NaN does not even === itself.

Homework: Read confusing special cases of NaN

Object Equality. Comparing objects

2 approaches for testing equality:

  1. Primitives (strings, numbers): are compared by their value

  2. Objects (arrays, dates, and user defined objects) are compared by their reference. Ie. referring to the same location in memory.

Equality check for objects will check whether two objects have same value for same property. To check that:

  1. you can get the keys for both the objects. If the number of properties doesn't match, these two objects are not equal.

  2. check each property whether they have the same value. If all the properties have same value, they are equal.

function isEqual(a, b) {
    var aProps = Object.getOwnPropertyNames(a),
        bProps = Object.getOwnPropertyNames(b);

    if (aProps.length != bProps.length) {
        return false;
    }

    for (var i = 0; i < aProps.length; i++) {
        var propName = aProps[i];
        
        if (a[propName] !== b[propName]) {
            return false;
        }
    }
    return true;
}

Limitation:

  • If one of the property values is itself an object

  • If one of the property values is NaN (the only value in JavaScript that is not equal to itself?)

  • If one object has a property with value undefined, while another object doesn't have the property (which thus evaluates as undefined). Btw, you can solve this problem by using hasOwnProperty

ref: object equality in JS, Underscore.js isEqual function

typeof vs instanceof

typeof is an operator that returns a string with the type of whatever you pass.

The typeof operator checks if a value belongs to one of the seven basic types: number, string, boolean, object, function, undefined or Symbol.

typeof(null) will return object.

instanceof is much more intelligent: it works on the level of prototypes. In particular, it tests to see if the right operand appears anywhere in the prototype chain of the left. instanceof doesn’t work with primitive types. It instanceofoperator checks the current object and returns true if the object is of the specified type, for example:

var dog = new Animal();
dog instanceof Animal; // Output : true

Here dog instanceof Animal is true since dog inherits from Animal.prototype

var name = new String("xyz");
name instanceof String; // Output : true

Function vs Method vs Constructor

Functions

The simplest usage:

function helloWorld(name) {
  return "hello world, " + name;
}

helloWorld("JS Geeks"); // "hello world JS Geeks"

Methods

Object properties that are functions.

var obj = {
  helloWorld : function() {
    return "hello world, " + this.name;
  },
  name: 'John Carter'
}
obj.helloWorld(); // // "hello world John Carter"

Notice how helloWorld refer to this properties of obj. Here it's clear or you might have already understood that thisgets bound to obj. But the interesting point that we can copy a reference to the same function helloWorld in another object and get a difference answer. Let see:

var obj2 = {
  helloWorld : obj.helloWorld,
  name: 'John Doe'
}
obj2.helloWorld(); // "hello world John Doe"

You might be wonder what exactly happens in a method call here. Here we call the expression itself determine the binding of this this, The expression obj2.helloWorld() looks up the helloWorld property of obj and calls it with receiver object obj2.

Constructors

Create and initialize a brand new object and passes it as the value of this

Implicitly returns the new object as its result.

function Employee(name, age) {
  this.name = name;
  this.age = age;
}

var emp1 = new Employee('John Doe', 28);
emp1.name; // "John Doe"
emp1.age; // 28

Ways of creating objects

1. Function based

Useful if we want to create several similar objects. In the code sample below, we wrote the function Employee and used it as a constructor by calling it with the new operator.

  function Employee(fName, lName, age, salary){
  	this.firstName = fName;
  	this.lastName = lName;
  	this.age = age;
  	this.salary = salary;
  }

  // Creating multiple object which have similar property but diff value assigned to object property.
  var employee1 = new Employee('John', 'Moto', 24, '5000$');
  var employee1 = new Employee('Ryan', 'Jor', 26, '3000$');
  var employee1 = new Employee('Andre', 'Salt', 26, '4000$');

2. Object Literal

The best way to create an object and this is used frequently. Below is code sample for create employee object which contains property as well as method.

var employee = {
	name : 'Nishant',
	salary : 245678,
	getName : function(){
		return this.name;
	}
}

The code sample below is Nested Object Literal, Here address is an object inside employee object.

var employee = {
	name : 'Nishant',
	salary : 245678,
	address : {
		addressLine1 : 'BITS Pilani',
		addressLine2 : 'Vidya Vihar'.
		phoneNumber: {
		  workPhone: 7098889765,
		  homePhone: 1234567898
		}
	}
}

3. new keyword

Example:

var employee = new Object(); // Created employee object using new keywords and Object()
employee.name = 'Nishant';
employee.getName = function(){
	return this.name;
}

4. Object.create()

Object.create(obj) will create a new object and set the obj as its prototype.

Modern way to create objects that inherit properties from other objects. Object.create function doesn’t run the constructor.

Use Object.create(null) when you don’t want your object to inherit the properties of Object.

Function statements vs Function expressions

Function statements

Defined at parse-time

// Parse-Time function declaration
bar(); // Call bar function here, It will not give an Error
function bar() {
  console.log("Hi I am inside Foo");
}

Function expressions

Defined at runtime

var-declared variables and functions are hoisted (JS interpreter looks ahead to find all the variable declaration and hoists them to the top of the function where it's declared).

// Run-Time function declaration
foo(); // Call foo function here, It will give an error
var foo = function() {
  console.log("Hi I am inside Foo");
};

IIFE's

Immediately Invoked Function Expression

Function that runs as soon as it's defined.

Usually it's anonymous, but it also can be named.

Example:

(function() {
  console.log("Hi, I'm IIFE!");
})();
// outputs "Hi, I'm IIFE!"

typeof() vs instanceOf()

typeof()

operator that returns a string with the type of whatever you pass.

Checks if a value belongs to one of the seven basic types: number, string, boolean, object, function, undefined or Symbol.

typeof(null) will return object.

instanceof()

Is much more intelligent: it works on the level of prototypes.

It tests to see if the right operand appears anywhere in the prototype chain of the left.

It doesn’t work with primitive types. It instanceofoperator checks the current object and returns true if the object is of the specified type

Functional vs Object Oriented programming

OOP Pros:

It’s easy to understand the basic concept of objects and easy to interpret the meaning of method calls. OOP tends to use an imperative style rather than a declarative style, which reads like a straight-forward set of instructions for the computer to follow.

OOP Cons

Depends on shared state. Objects and behaviors are typically tacked together on the same entity, which may be accessed at random by any number of functions with non-deterministic order, which may lead to undesirable behavior such as race conditions.

FP Pros

FP avoids any shared state or side-effects: eliminates bugs caused by multiple functions competing for the same resources. With features such as the availability of point-free style (aka tacit programming), functions tend to be radically simplified and easily recomposed for more generally reusable code compared to OOP.

Favors declarative and denotational styles, which do not spell out step-by-step instructions for operations, but instead concentrate on what to do, letting the underlying functions take care of the how. This leaves tremendous latitude for refactoring and performance optimization, even allowing you to replace entire algorithms with more efficient ones with very little code change. (e.g., memoize, or use lazy evaluation in place of eager evaluation.)

Easy to scale across multiple processors, or across distributed computing clusters without fear of threading resource conflicts, race conditions, etc…

FP Cons

Over exploitation can potentially reduce readability because the resulting code is often more abstractly specified, more terse, and less concrete.

More people are familiar with OO and imperative programming than functional programming, can be confusing.

Steeper learning curve than OOP.

Closures

Inner function that has access to the variables in the outer (enclosing) function’s scope chain, even after the outer function has returned.

To use a closure: define a function inside another function and expose it (return it or pass it to another function).

Have access to variables in 3 scopes:

  1. Its own scope

  2. The enclosing function’s scope

  3. Global variables

Example:

var globalVar = "xyz";

(function outerFunc(outerArg) {
    var outerVar = 'a';
    
    (function innerFunc(innerArg) {
    var innerVar = 'b';
    
    console.log(
        "outerArg = " + outerArg + "\n" +
        "innerArg = " + innerArg + "\n" +
        "outerVar = " + outerVar + "\n" +
        "innerVar = " + innerVar + "\n" +
        "globalVar = " + globalVar);
    
    })(456);
})(123);

Variables from innerFunc, outerFunc, and the global namespace are all in scope in the innerFunc.

Output:

outerArg = 123
innerArg = 456
outerVar = a
innerVar = b
globalVar = xyz

Uses

  1. To give objects data privacy: the enclosed variables are only in scope within the containing (outer) function. You can’t get at the data from an outside scope except through the object’s privileged methods.

  2. To create stateful functions whose return values may be influenced by their internal state, e.g.: const secret = msg => () => msg;

  3. In functional programming: for partial application & currying

Currying

Pattern where a function with more than one parameter is broken into multiple functions that, when called in series, will accumulate all of the required parameters one at a time.

Useful for making code written in a functional style easier to read and compose.

For a function to be curried, it needs to start out as one function, then broken out into a sequence of functions that each accepts one parameter.

function curry(fn) {
  if (fn.length === 0) {
    return fn;
  }

  function _curried(depth, args) {
    return function(newArgument) {
      if (depth - 1 === 0) {
        return fn(...args, newArgument);
      }
      return _curried(depth - 1, [...args, newArgument]);
    };
  }

  return _curried(fn.length, []);
}

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

var curriedAdd = curry(add);
var addFive = curriedAdd(5);

var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]

References

Monolithic vs Microservice architectures

Monolithic architecture

App is written as one cohesive unit of code whose components are designed to work together, sharing the same memory space and resources.

Pros

  1. Most apps typically have a large number of cross-cutting concerns, such as logging, rate limiting, and security features such audit trails and DOS protection. When everything is running through the same app, it’s easy to hook up components to those cross-cutting concerns.

  2. Performance advantages, since shared-memory access is faster than inter-process communication (IPC).

Cons

  1. Tightly coupled and entangled as the application evolves. Difficult to isolate services for purposes such as independent scaling or code maintainability.

  2. Harder to understand: dependencies, side-effects.

Microservice architecture

App is made up of lots of smaller, independent applications capable of running in their own memory space and scaling independently from each other across potentially many separate machines.

Microservice pros

  1. Better organized. Each microservice has a very specific job, and is not concerned with the jobs of other components. Decoupled services are also easier to recompose and reconfigure to serve the purposes of different apps.

  2. Performance advantages: independently scalable microservices.

Microservice cons

  1. As you’re building a new microservice architecture, you’re likely to discover lots of cross-cutting concerns that you did not anticipate at design time. A monolithic app could establish shared magic helpers or middleware to handle such cross-cutting concerns without much effort.

  2. Overhead of separate modules for each cross-cutting concern, or encapsulate cross-cutting concerns in another service layer that all traffic gets routed through. Eventually, even monolthic architectures tend to route traffic through an outer service layer for cross-cutting concerns, but with a monolithic architecture, it’s possible to delay the cost of that work until the project is much more mature.

  3. Frequently deployed on their own virtual machines or containers, causing a proliferation of VM wrangling work. These tasks are frequently automated with container fleet management tools.

  4. Performance overhead caused by IPC and network communication.

Two-way data binding vs One-way data flow

Two-way data binding

  • UI fields are bound to model data dynamically

  • When a UI field changes, the model data changes with it and vice-versa.

  • Example: Angular

One-way data flow

  • The model is the single source of truth.

  • Changes in the UI trigger messages that signal user intent to the model (or “store” in React)

  • Only the model has the access to change the app’s state. The effect is that data always flows in a single direction, which makes it easier to understand.

  • Example: React

Event delegation

Adding event listeners to a parent element instead of adding them to the descendant elements.

The listener will fire whenever the event is triggered on the descendant elements due to event bubbling up the DOM.

Attach a single event listener, to a parent element, that will fire for all children matching a selector, whether those children exist now or are added in the future. The underlying mechanism is browser's event bubbling .

Benefits:

  • Memory footprint goes down. Only one single handler is needed on the parent element, rather than having to attach event handlers on each descendant.

  • No need to unbind the handler from elements that are removed and to bind the event for new elements.

References

Event bubbling

When an event triggers on a DOM element, it will attempt to handle the event if there is a listener attached, then the event is bubbled up to its parent and the same thing happens. This bubbling occurs up the element's ancestors all the way to the document.

The mechanism behind event delegation.

Code organization (modeula pattern, classical inheritance)

call() vs apply()

Both .call and .apply are used to invoke functions and the first parameter will be used as the value of this within the function.

.call takes in comma-separated arguments as the next arguments

.apply takes in an array of arguments as the next argument.

Way to remember this is C for call and comma-separated and A for apply and an array of arguments.

strict mode

'use strict'; enables strict mode to entire scripts or individual functions.

Strict mode is a way to opt into a restricted variant of JavaScript.

Advantages:

  • Impossible to accidentally create global variables.

  • Assignments which would otherwise silently fail to throw an exception.

  • Attempts to delete undeletable properties throw (where before the attempt would simply have no effect).

  • Requires that function parameter names be unique.

  • this is undefined in the global context.

  • Catches some common coding bloopers, throwing exceptions.

  • Disables features that are confusing or poorly thought out.

Disadvantages:

  • Many missing features that some developers might be used to.

  • No more access to function.caller and function.arguments.

  • Concatenation of scripts written in different strict modes might cause issues.

Overall, I think the benefits outweigh the disadvantages.

Cloning an object

var obj = {a: 1 ,b: 2}
var objclone = Object.assign({},obj);

Now the value of objclone is {a: 1 ,b: 2} but points to a different object than obj.

Note the potential pitfall, though: Object.clone() will just do a shallow copy, not a deep copy. This means that nested objects aren’t copied. They still refer to the same nested objects as the original:

let obj = {
    a: 1,
    b: 2,
    c: {
        age: 30
    }
};

var objclone = Object.assign({},obj);
console.log('objclone: ', objclone);

obj.c.age = 45;
console.log('After Change - obj: ', obj);           // 45 - This also changes
console.log('After Change - objclone: ', objclone); // 45

Iterating over object properties and array items

Objects:

  • for-in loops - for (var property in obj) { console.log(property); }. However, this will also iterate through its inherited properties, and you will add an obj.hasOwnProperty(property) check before using it.

  • Object.keys() - Object.keys(obj).forEach(function (property) { ... }). Object.keys() is a static method that will lists all enumerable properties of the object that you pass it.

  • Object.getOwnPropertyNames() - Object.getOwnPropertyNames(obj).forEach(function (property) { ... }). Object.getOwnPropertyNames() is a static method that will lists all enumerable and non-enumerable properties of the object that you pass it.

Arrays:

  • for loops - for (var i = 0; i < arr.length; i++). The common pitfall here is that var is in the function scope and not the block scope and most of the time you would want block scoped iterator variable. ES2015 introduces letwhich has block scope and it is recommended to use that instead. So this becomes: for (let i = 0; i < arr.length; i++).

  • forEach - arr.forEach(function (el, index) { ... }). This construct can be more convenient at times because you do not have to use the index if all you need is the array elements. There are also the every and some methods which will allow you to terminate the iteration early.

  • for-of loops - for (let elem of arr) { ... }. ES6 introduces a new loop, the for-of loop, that allows you to loop over objects that conform to the iterable protocol such as String, Array, Map, Set, etc. It combines the advantages of the for loop and the forEach() method. The advantage of the for loop is that you can break from it, and the advantage of forEach() is that it is more concise than the for loop because you don't need a counter variable. With the for-of loop, you get both the ability to break from a loop and a more concise syntax.

Most of the time, I would prefer the .forEach method, but it really depends on what you are trying to do. Before ES6, we used for loops when we needed to prematurely terminate the loop using break. But now with ES6, we can do that with for-of loops. I would use for loops when I need even more flexibility, such as incrementing the iterator more than once per loop.

When using the for-of loop, if you need to access both the index and value of each array element, you can do so with the ES6 Array entries() method and destructuring:

const arr = ['a', 'b', 'c'];

for (let [index, elem] of arr.entries()) { 
  console.log(index, ': ', elem);
}

Add an element at the beginning / end of an array

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

With ES6, one can use the spread operator:

myArray = ['start', ...myArray];
myArray = [...myArray, 'end'];

Or, in short:

myArray = ['start', ...myArray, 'end'];

Last updated