Skip to main content

· 5 min read
Nishant Mendiratta

Here's a brief overview of important JavaScript concepts, along with sample code snippets for each:

1. Hoisting

Hoisting refers to JavaScript's behavior of moving declarations (variables and functions) to the top of their scope before code execution.

console.log(a);  // undefined due to hoisting
var a = 5;

2. Single Threaded

JavaScript runs in a single thread, meaning it can only execute one task at a time.

3. Synchronous

Synchronous code is executed line by line, with each operation blocking the next one until it completes.

console.log("First");
console.log("Second");

4. Function Invocation

This is the process of calling or executing a function.

function greet() {
console.log("Hello");
}
greet(); // Function invocation

5. Variable Environment

The context in which variables are stored and accessed, typically inside a scope (global, function, or block).

6. Scope

Scope determines the visibility and lifetime of variables and functions. In JavaScript, there are function scope, block scope, and global scope.

if (true) {
let x = 5; // Block scope
}
console.log(x); // Error: x is not defined

7. Asynchronous

Asynchronous code allows other operations to continue while waiting for a task to complete.

setTimeout(() => {
console.log("Asynchronous");
}, 1000);

8. How This Happens Inside JS Engine?

JavaScript's event loop handles asynchronous operations. The call stack executes synchronous code, while the event loop waits for tasks in the callback queue.

9. Dynamic Typing

In JavaScript, variable types are determined at runtime, allowing flexibility in assigning different types to the same variable.

let data = 42;
data = "Now a string"; // No error

10. Operators

Operators in JavaScript include arithmetic, comparison, logical, etc.

let sum = 10 + 5;  // Arithmetic
let isEqual = 10 === 10; // Comparison

11. Operator Precedence

This defines the order in which operations are executed.

let result = 2 + 3 * 4;  // 14, because * has higher precedence than +

12. Associativity

Associativity determines the direction in which operators are evaluated.

let result = 10 - 5 - 2;  // Left-associative, evaluated as (10 - 5) - 2

13. Coercion

JavaScript automatically converts types when required.

let result = '5' - 2;  // '5' is coerced into a number, result is 3

14. Namespace

A way to organize code by grouping related variables and functions.

let MyApp = {
name: "My Application",
version: 1.0
};

15. First-Class Function (FCF)

Functions in JavaScript can be treated as variables and passed around as arguments or returned from other functions.

function sayHello() {
return "Hello";
}
let greet = sayHello; // First-class function

16. Expression

An expression is any valid unit of code that resolves to a value.

let sum = 5 + 10;  // The expression `5 + 10` evaluates to 15

17. Executing Function Created on the Fly

Anonymous functions are created and executed instantly.

(function() {
console.log("Executed on the fly");
})();

18. Mutate

To modify an object or variable's properties.

let obj = { name: "Alice" };
obj.name = "Bob"; // Mutating the object

19. Arguments

arguments is an array-like object in functions that contains all the arguments passed.

function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(sum(1, 2, 3)); // 6

20. Whitespace

Whitespace (spaces, tabs, newlines) is ignored in JavaScript code for the most part.

21. Closure

A closure is a function that remembers and accesses variables from its outer scope, even after that scope has closed.

function outer() {
let x = 10;
return function inner() {
console.log(x);
};
}
const closureFunc = outer();
closureFunc(); // Outputs 10

22. Factory

A function that returns objects.

function createPerson(name, age) {
return { name, age };
}
const person = createPerson("John", 30);

23. Callback Function

A function passed as an argument to another function and executed later.

function fetchData(callback) {
setTimeout(() => {
callback("Data fetched");
}, 1000);
}

fetchData((message) => console.log(message));

24. Function Currying

Currying transforms a function that takes multiple arguments into a series of functions, each taking one argument.

function add(a) {
return function(b) {
return a + b;
};
}
let add5 = add(5);
console.log(add5(3)); // 8

25. Inheritance

Inheritance allows one object to acquire properties and methods of another.

class Animal {
speak() {
console.log("Animal speaks");
}
}

class Dog extends Animal {
bark() {
console.log("Dog barks");
}
}

26. Function Constructors

A way to create objects in JavaScript using constructor functions.

function Person(name, age) {
this.name = name;
this.age = age;
}
const john = new Person("John", 30);

27. Polyfill

A polyfill is code that provides functionality for older browsers that don't natively support newer features.

if (!Array.prototype.includes) {
Array.prototype.includes = function(element) {
return this.indexOf(element) !== -1;
};
}

28. Syntactic Sugar

Syntactic sugar refers to language features that don't add new functionality but make the code easier to read and write.

// Arrow function is syntactic sugar for writing function expressions
const add = (a, b) => a + b;
info

If you are looking for a resource where you can learn Javascript. Summarized version of Understanding the Weird Parts is highly recommended.

· 3 min read
Nishant Mendiratta

Currying Vs Partial application.

In simple words: Currying is converting a single function of n arguments into n functions with a single argument each. Given the following function:

function f(x,y,z) {
z(x(y));
}

When curried, becomes:

function f(x) {
lambda(y) {
lambda(z) {
z(x(y));
}
}
}

In order to get the full application of f(x,y,z), you need to do this:

f(x)(y)(z);

Many functional languages let you write f x y z.

If you only call f x y or f(x)(y) then you get a partially-applied function—the return value is a closure of lambda(z){ z(x(y)) } with passed-in the values of x and y to f(x,y).

Example 1

One way to use partial application is to define functions as partial applications of generalized functions, like fold:

function fold(combineFunction, accumulator, list) {/* ... */}
function sum = curry(fold)(lambda(accum,e){e+accum}))(0);
function length = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);

/*
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list)
f(0,list) //returns 10
@g = f(0) //same as sum
g(list) //returns 10
*/

Example 2

The easiest way to see how they differ is to consider a real example. Let's assume that we have a function Add which takes 2 numbers as input and returns a number as output, e.g. Add(7, 5) returns 12. In this case:

Partial applying the function Add with a value 7 will give us a new function as output. That function itself takes 1 number as input and outputs a number. As such:

Partial(Add, 7); // returns a function f2 as output
// f2 takes 1 number as input and returns a number as output

So we can do this:

f2 = Partial(Add, 7);
f2(5); // returns 12;
// f2(7)(5) is just a syntactic shortcut

Currying the function Add will give us a new function as output. That function itself takes 1 number as input and outputs yet another new function. That third function then takes 1 number as input and returns a number as output. As such:

Curry(Add); // returns a function f2 as output

// f2 takes 1 number as input and returns a function f3 as output
// i.e. f2(number) = f3

// f3 takes 1 number as input and returns a number as output
// i.e. f3(number) = number

So we can do this:

f2 = Curry(Add);
f3 = f2(7);
f3(5); // returns 12

In other words, "currying" and "partial application" are two totally different functions. Currying takes exactly 1 input, whereas partial application takes 2 (or more) inputs.

Even though they both return a function as output, the returned functions are of totally different forms as demonstrated above.

Resources

SO

· 5 min read
Nishant Mendiratta

var vs let vs. const. What's the Difference?

To analyze the differences between these keywords, I'll be using three factors:

  • Scope of variables
  • Redeclaration and reassignment
  • Hoisting

Var

  • Variables declared with var can have a global or local scope.
  • Global scope is for variables declared outside functions, while local scope is for variables declared inside functions.
  • Variables declared with var can be redeclared and reassigned.
  • Variables declared with var are hoisted to the top of their global or local scope, which makes them accessible before the line they are declared. But the variable is hoisted with a default value of undefined.
var a = 100;
let b = 200;
const c = 300;
{
var a = 1; // a is shadowing 'a' in line 1 (both 'a' are pointing to same memory location) // GLOBAL memory
let b = 2; // block memory
const c = 3; // block memory

console.log(a); // 1
console.log(b); // 2 // shadowing because of block memory
console.log(c); // 3 //
}

console.log(a); // 1 shadowed and modified line 1, both 'a' are pointing to same memory location)
console.log(b); // 200 // Not shadowing because of block memory, diff memory space
console.log(c); // 300 // Not shadowing because of block memory, diff memory space

Let

  • Variables declared with let can have a global, local, or block scope.
  • Block scope is for variables declared in a block. A block in JavaScript involves opening and closing curly braces.
  • Just like var, variables declared with let can be reassigned to other values, but they cannot be redeclared.
  • Redeclaring a variable with let will throw an error. You see we get a syntax error: Identifier 'number' has already been declared.
var a = 100;
let b = 200;
const c = 300;
{
var a = 1; // a is shadowing 'a' in line 1 (both 'a' are pointing to same memory location) // GLOBAL memory
let b = 2; // block memory
const c = 3; // block memory

console.log(a); // 1
console.log(b); // 2 // shadowing because of block memory
console.log(c); // 3 //
}

console.log(a); // 1 shadowed and modified line 1, both 'a' are pointing to same memory location)
console.log(b); // 200 // Not shadowing because of block memory, diff memory space
console.log(c); // 300 // Not shadowing because of block memory, diff memory space

Const

  • Variables declared with const are similar to let in regards to scope. Such variables can have a global, local, or block scope.
  • In this regard, const is different from var and let. const is used for declaring constant variables – which are variables with values that cannot be changed.
  • So such variables cannot be redeclared, and neither can they be reassigned to other values. Attempting such would throw an error.
    • SyntaxError: Identifier 'number' has already been declared
    • TypeError: Assignment to constant variable
  • Variables declared with const, just like let, are hoisted to the top of their global, local, or block scope – but without a default initialization.
  • Accessing a variable declared with const before the line of declaration will throw a cannot access variable before initialization error.
    • ReferenceError: Cannot access 'number' before initialization
var a = 100;
let b = 200;
const c = 300;
{
var a = 1; // a is shadowing 'a' in line 1 (both 'a' are pointing to same memory location) // GLOBAL memory
let b = 2; // block memory
const c = 3; // block memory

console.log(a); // 1
console.log(b); // 2 // shadowing because of block memory
console.log(c); // 3 //
}

console.log(a); // 1 shadowed and modified line 1, both 'a' are pointing to same memory location)
console.log(b); // 200 // Not shadowing because of block memory, diff memory space
console.log(c); // 300 // Not shadowing because of block memory, diff memory space
Summary

Here's a table summary showing the differences between these keywords:

KEYWORDSCOPEREDECLARATION & REASSIGNMENTHOISTING
varGlobal, Localyes & yesyes, with default value
letGlobal, Local, Blockno & yesyes, without default value
constGlobal, Local, Blockno & noyes, without default value

These factors I've explained, play a role in determining how you declare variables in JavaScript.

If you never want a variable to change, const is the keyword to use.

If you want to reassign values:

  • and you want the hoisting behavior, var is the keyword to use
  • if you don't want it, let is the keyword for you

The hoisting behavior can cause unexpected bugs in your application. That's why developers are generally advised to avoid var and stick to let and cost.

Resources

Differences Between These Keywords

· 5 min read
Nishant Mendiratta

A fun little quiz. The quizzes get more and more difficult, and they present a nice challenge if you fear your skills are getting rusty. Give it an honest try first, then come back here if you get seriously, honestly stuck. Try to do it yourself!

Problem 1: doubleInteger

function doubleInteger(i) {
// i will be an integer. Double it and return it.

return i * 2;
}

Explanation

Nothing tricky going on here. The prompt implies this should be done in two steps, but a one-liner works. We know i will always be an integer, so we don’t need any sanitization checks, just a straight up return.

Problem 2: isNumberEven

function isNumberEven(i) {

// i will be an integer. Return true if it's even,
// and false if it isn't.

return i % 2 === 0;
}

Explanation

The solution relies on an understanding of the modulus function. Basically, think of modulus as “remainder” function for division problems. For instance, 4 % 2 will give a modulus of 1 because 4 goes into 2 twice, evenly, with zero remainder. On the other hand, 5 % 2 will give a modulus of 1 because 5 goes into 2 twice, with a remainder of 1. This turns out to be a handy way to differentiate even (4) and odd (5) numbers.

Problem 3: getFileExtension

function getFileExtension(i) {

// i will be a string, but it may not have a file extension.
// return the file extension (with no period) if it has one,
// otherwise false

var e = i.search(/\..+/)
if (e !== -1) {
return i.substr(e + 1);
}
else {
return false;
}
}

// Although above passes the test,
// this is a much more reliable solution.
function getFileExtension(i) {
var result = i.split('.');
return result.length > 1 ? result.pop() : false;
}

Explanation

Now we’re getting a bit tougher! Strings have a handy function that splits them into an array: split(). For instance, "foo.bar.baz".split(".") will output ["foo", "bar", "baz"]. Notice that the periods have been removed, just as the prompt asked for!

In this case we need to first check if there was no file extension. This means that there were no periods to split on, resulting in a one-member array ("foo".split(".") becomes ["foo"]). In this case we return false, as the prompt asks for.

Now, in the else case, we know that the array must be larger than 1, meaning that it has a file extension. Since the file extension is always at the end, we know that it must be the last member in the array.

We can find the index of the last member by taking into account array length (result.length) and the fact that the array is zero-indexed, meaning that we need to subtract 1 from this length. Now we know the extension exists and is at the end of the array, so all we need to do is return it:

Problem 4: longestString

function longestString(i) {

// i will be an array.
// return the longest string in the array

var longest = ''
if (Array.isArray(i)) {
for (var n = 0, len = i.length; n < len; ++n) {
if ((typeof i[n] === 'string') &&
(i[n].length > longest.length)) {
longest = i[n];
}
}
}
return longest;
}

// Alternate solution with some
// functional programming thrown in.
function longestString(i) {
var longest = '';
if (Array.isArray(i)) {
var a = i.filter(function(t) {
return typeof t === 'string';
}).forEach(function(t) {
if (t.length > longest.length) {
longest = t;
}
});

return longest;
}
}

Explanation

Just keep track of the longest string in a variable, then loop through all the members of the array. If the current member is longer, set it as the new longest string. At the end, return whatever that variable contains.

Problem 5: arraySum

function arraySum(i) {

// i will be an array, containing integers, strings
// and/or arrays like itself. Sum all the integers you
// find, anywhere in the nest of arrays.

var sum = 0;
if (Array.isArray(i)) {
for (var n = 0, len = i.length; n < len; ++n) {
if (Array.isArray(i[n])) {
sum += arraySum(i[n]);
}
else if (typeof i[n] === 'number') {
sum += i[n];
}
}
}
return sum;
}

Explanation

The best way to handle this one is through recursion, which is tricky but powerful.

First, let’s start with arraySum(1). The output is of course 1, but note that we didn’t actually input an array. The code took the first codepath in the if statement (because typeof 1 === "number" is true)

Let’s see what happens when we input a basic array:

arraySum([1]); // 1

Again the answer is 1, but this time the codepath was different. This time Array.isArray([1]) was true, and the function was called recursively for each of the members in the array. That is to say, each member of the array (in this case the single member) was sent to arraySum as a simple integer. So the recursive call was the same as the original example: arraySum(1), and the output was the same.

In the case of more numbers, the outputs are all added to the sum and then returned back at the end of the function.

Note that this works quite well for even deeply nested arrays, since arraySum() has logic for arrays that will always return the sum of all of its deeply nested integers:

Resources

· 2 min read
Nishant Mendiratta

Question:

Write custom function to simulate implementation of double click.

Solution:

Use this solution only when you want to bind a single click AND a double click to an element. Otherwise I recommend using the native click and dblclick listeners.

These are the differences:

  • Vanillajs, No dependencies
  • Don't wait on the setTimeout to handle the click or doubleclick handler
  • When double clicking it first fires the click handler, then the doubleclick handler

Click:

  • Fires when a mousedown and mouseup event occur on the same element.
  • Fires when the user activates the element that has the keyboard focus (usually by pressing Enter or the space bar).

dblclick

  • Fires when two mouse click events occur on the same element within a reasonable time. In our solution we have taken 400 ms.

Code:

var singleClick = function () {
console.log('Single');
}
var doubleClick = function () {
console.log('Double');
}

function makeDoubleClick(singleClickCallBack, doubleClickCallBack) {
let clicks = 0, timeout;
return function (){
clicks++;
if (clicks === 1) {
singleClickCallBack.apply(this, arguments);
timeout = setTimeout(() => clicks = 0, 400);
} else {
clearTimeout(timeout);
doubleClickCallBack.apply(this,arguments);
clicks = 0;
}

}
}

document.addEventListener('click', makeDoubleClick(singleClick,doubleClick));

Latest Solution:

Instead of utilizing more ad-hoc states and setTimeout, turns out there is a native property called detail that you can access from the event object! Modern browsers and even IE-9 supports it :) Source

element.onclick = event => {
if (event.detail === 1) {
// it was a single click
} else if (event.detail === 2) {
// it was a double click
}
};

Resources:

· 5 min read
Nishant Mendiratta

Event Loop Phases in Node.js

The event loop has several different phases to it and each one of these phases maintains a queue of callbacks that are to be executed.

Callbacks are destined for different phases based on how they are used by the application.

Phase 1: Poll

  • Starting point of Node.js application
  • Most of the application code executes in this phase.
  • The poll phase executes I/O-related callbacks.

Phase 2: Check

  • In this phase, callbacks that are triggered via setImmediate() are executed.

Phase 3: Close

  • This phase executes callbacks triggered via EventEmitter close events.
  • For example, when a net.Server TCP server closes, it emits a close events that runs in this phase.

Phase 4: Timers

  • In this phase, callbacks triggered via setTimeout() and setInterval() are executed.

Phase 5: Pending

  • Special system events are run in this phase, like when a net.Socket TCP soccer throws an ECONNREFUSED error.

Microtask queues

Apart from these, there are two special microtask queues that can have callbacks added to them while a phase is running.

  • The first microtask queue handles callbacks registered using process.nextTick().
  • The second microtask queues handles promises that reject or resolve.

Execution Priority and order

  • Callback in the microtask queues take priority over callbacks in the phase's normal queue.
  • Callbacks in the next tick microtask queue run before callbacks in the promise microtask queue.
  • When the application starts running, the event loop is also started and the phases are handled one at a time. Node.js adds callbacks to different queues as appropriate while the application runs.
  • When the event loop gets to a phase, it will run all the callbacks in the phase's queue. Once all the callbacks in a given phase are executed, the event loop then moves on to the next phase.

Let's see one code example:

const fs = require('fs');

setImmediate(() => console.log(1));

Promise.resolve().then(() => console.log(2));

process.nextTick(() => console.log(3));

fs.readFile(__filename, () => {
console.log(4);
setTimeout(() => console.log(5));
setImmediate(() => console.log(6));
process.nextTick(() => console.log(7));
});

console.log(8);

Output will be 8 3 2 1 4 5 6 5

Let's see what is happening behind the scene:

Code execution starts off executing line by line in the poll phase.

Step 1: The fs module is required. Step 2: The setImmediate() call is run and its callback is added to the check queue. Step 3: the promise resolves, adding callback to the promise microtask queue. Step 4: process.nextTick() runs next, adding its callback to the next tick microtask queue. Step 5: The fs.readFile() tells Node.js to start reading the file, placing its callback in the poll queue once it is ready. Step 6: Finally console.log(8) is called and 8 is printed to the screen.

That's it for the current stack.

  • Now, the two microtask queues are consulted. The next tick microtask queue is always checked first, and callback with output 3 is called. Since, there is only one callback in the next tick microtask queue, the promise microtask queue is checked next and callback with output 2 is executed. That finished the two micro-task queues and the current poll phase is completed.
  • Now, the event loop enters the check phase. This phase has callback 1 in it, which is then executed. Both the microtask queues are empty at this point, so the check phase ends.
  • The close phase is checked next but is empty, so the loop continues. The same happens with the timers phase and the pending phase, and the event loop continues back around to the poll phase.

Once it is back in the poll phase, the application doesn't have much else going on, so it basically waits until the file has finished being read. Once that happens, the fs.readFile() callback is run.

  • The number 4 is immediately printed since it's the first line in the callback.
  • next, the setTimeout() call is made and callback 5 is added to the timers queue.
  • The setImmediate() call happens next, adding callback 6 to the check queue.
  • Finally, the process.nextTick() call is made, adding callback 7 to the next ticket microtask queue.

The poll phase is now finished and the microtask queues are again consulted.

  • Callback 7 runs from the next tick queue,
  • The promise queue is consulted and found empty, and the poll phase ends.
  • Again the event loop enters to the check phase where callback 6 is encountered. The number is printed and microtask queues are determined to be empty and the phase ends.
  • The close phase is checked again and found empty.
  • Finally, the timers phase is consulted and callback 5 is executed and prints 5 on the console.
  • Once that's done, the applications doesn't have any more work to do and it exits.

As we know, Node.js runtime environment is single-threaded. Running too much code in a single stack will stall the event loop and prevent other callbacks from firing.

tip

To prevent this event loop starving situation, you can break your CPU-heavy operations up across multiple stacks.

For example, if you are processing 1000 data records, you can consider breaking down into 10 batches of 100 records, using setImmediate() at the end of each batch to continue processing the next batch.

Another option is forking a new child process and offload processing to it. But never break up such work using process.nextTick(). Doing so will lead to a microtask queue that never empties and your application will be trapped in the same phase forever. The runtime won't throw any error instead it will remain a zombie process that eats through CPU.

Reference: Distributed Systems with Node.js (Book)

· 2 min read
Nishant Mendiratta

Can you write code for this function: sum(a)(b)(c)....( n)(). This should return the sum of all the numbers a+b+c+..+n.


// Functions in javascripts are First Class Functions
// Functions are like objects
// sum(1)(2)(3).. (n)

// Fist consider it like a function sum(1)(2)() which is called and gives output 3
let sum = function (a) {
// Sum again gives a function which can be executed by passing an argument inside it let's call it b
return function (b) {
return a+b
}
}

// Now consider it like a function sum(1)(2)(3)() which is called and gives output 6
let sum = function (a) {
return function (b) {
return function (c) {
return a+b+c;
}
}
}


// We can see a clear pattern, will solve it recursively
const sum = function (a) {
return function (b) {
if (b) {
return sum(a+b); // it takes an argument and return a function which again can take an argument.
}
return a; // it will keep on adding 1+2+3+4..
}
};


console.log(sum(1)(2)()); //3
console.log(sum(1)(2)(3)(4)()); //10

Github

Follow me on Github.


Bookmark

Unlike life, keyboards do have shortcuts, press COMMAND+D to make this an easily accessible resource by bookmarking it.


Original Post