Javascript important concepts that every web developer should know

Prasad Sonawane
14 min readApr 8, 2021

We are going to cover here some javascript important and foundational concepts that need to know every javascript developer.

Please hit claps 👏 if you love this post and share it with your friends.

Below is the list we are going to cover in this post :

  1. Callstack
  2. Event Loop
  3. Callback function
  4. Promises
  5. Async & Await
  6. IIFE
  7. Scope
  8. Hoisting
  9. this and apply, call, bind methods
  10. Higher-order functions
  11. Currying
  12. Pure Functions
  13. Debouncing and Throttling
  14. Deep and shallow copying
  15. Event Bubbling
  16. Event Capturing
  17. Function expression and Function declaration
  18. use strict
  19. Identity Operator (===) vs. Equality Operator (==)
  20. Generators
  21. Array methods
  22. Map Set
  23. Closure

1. Callstack

We know JavaScript is a single-threaded programming language, which means it has a single Call Stack. Therefore it can do one thing at a time.

single thread == single call stack == single task at a time

It keeps track of what function is currently being run and what functions are called from within that function.

2. Event Loop

Now we know that call stack is actually where we are in the program and javascript is single-threaded so consider the below example-

function first() {
console.log('first');
}
function second() {
console.log('second');
}
function third() {
console.log('third');
}
first();
setTimeout(second, 1000); // Invoke `second` after 1000ms
third();

So event loop has one simple job to monitor the call stack and the call queue. If the call stack is empty it will take the first event from the queue and will push it to the call stack.

3. callback

The callback is a function that executes after another function has executed.

A function that waits for another function to execute or return value is referred to as a callback.

A callback function is a function passed into another function as an argument which is then invoked inside the outer function.

WHY Callback?

  • Wait for the event to execute
  • Provides synchronous capability
  • chain functionality
  • provide code structure and control
  • Instead of waiting keep executes another
function first(){
console.log(1);
}
function second(){
console.log(2);
}
first();
second();
//output:
1
2

Let's add set a timeout in the above code

function first(){
setTimeout(function(){
console.log(1);
},500)
}
function second(){
console.log(2);
}
first();
second();
//output:
2
1

So callbacks are the way to make sure certain code doesn't execute until another code has already finished executing.

function doHomework(subject, callback){
console.log(`starting homework of ${subject}`);
callback();
}
function alertFinished(){
console.log(“finished”);
}
doHomework(“math”, alertFinished);

4. Promises

Once you understand callback you will know the callback hell, to handle callback promises comes in picture.

promises used to handle code asynchronously.

A Promise is in one of these states:
pending: initial state, neither fulfilled nor rejected.
fulfilled: meaning that the operation completed successfully.
rejected: meaning that the operation failed.

MDN
var keepsHisWord;
keepsHisWord = true;
promise1 = new Promise(function(resolve, reject) {
if (keepsHisWord) {
resolve("The man likes to keep his word");
} else {
reject("The man doesnt want to keep his word");
}
});
console.log(promise1);

5. Async & Await

It provides special syntax to use promises and easy to understand
Async

async function asyncFunc() {
return "Hey!";
}

an async function always returns promises and return value automatically wrapped into resolved promise.

async function myfunction() {
return "Hello!";
}

myfunction().then(alert); // Hello

Await

Await is only works with async block, which means you can't use await at the top level of your code.

if you have an asynchronous function inside of an async block you can use Await.

async function myfunction() {
// fetch data from api
const res = await axios.get("/api");
return res;
}

the above code can be written without await as below

async function myfunction() {
let res;
// fetch data from api
axios.get(“/api”)
.then((result) => {
res = result
});
return res;
}

But Await is a more simple way to write promises inside an async function.

6. IIFE

IIFE stands for an Immediately invoked function expression.

It is the function that gets executed after its creation or declaration.

Syntax:

(function () 
{
// code
})
();

Any variables declared within the IIFE cannot be accessed by the outside.

(function () 
{ var name = “Welcome”;
console.log(name);
})
();
console.log(name); //Error: name is not defined

7. Scope

The scope is the accessibility of your variable, functions, objects in some particular part of your code.

There are 2 main ways to defined scope

  1. Global Scope: Allow access outside the box
  2. Local Scope: Allow access inside the box

Global scope

var name =’sachin’;
(function(){
console.log(name); //Output: sachin
})();

Local Scope

(function(){
var name =’sachin’;
})();
console.log(name); //Output: Error name not defined

Benefits:

  1. Improves efficiency
  2. Track bugs and reduce them
  3. Solve naming problems so we can use the same name in different scope.

8. Hoisting

Hoisting means variable and functions declaration is moved to the top of their scope before code execution.

In JavaScript, an undeclared variable is assigned the value undefined at execution and is also of type undefined.

console.log(typeof variable); // Output: undefined

In JavaScript, a ReferenceError is thrown when trying to access a previously undeclared variable.

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

Let's understand by an example :

let x = 10;
function getName(){
console.log(“Javascript”);
}
getName();
console.log(x);
//output
Javasscript
10

let's make few changes in code as a cut paste to the top

getName();
console.log(x);
let x = 10;
function getName(){
console.log(“Javascript”);
}

//output
Javascript
Error: Cannot access 'x' before initialization

lets comment let x =10;

getName();
console.log(x);
//let x = 10;
function getName(){
console.log("Javascript");
}
//output
Javascript
Error: x is not defined

The operations result in the hoisting concepts.

9. apply, call and bind methods

Before understanding these methods first understand this,

The this keyword allows you to reuse functions with different contexts or the “this” keyword allows you to decide which object should be used when invoking a function or a method.

const user = {
name: 'sachin',
age: 30,
greet() {
alert(`Hello, my name is ${this.name}`)
}
}
user.greet() //sachin

In the above example, this refers to the user object and it is implicit binding, so whenever we want to check what is a current reference of this, we need to check what is on the left side of the dot.

function greet () {
alert(`Hello, my name is ${this.name}`)
}

const user = {
name: 'sachin',
age: 30,
}

In the above example, we don't invoke it like the first example because the user doesn't have the greet() method, so we can handles this by using the call() method.

We know javascript has an object, properties, and methods, and each object has properties and methods.

So we can have common methods for each object and we can use them by using apply/call/bind methods.

call method

“call” is a method on every function that allows you to invoke the function specifying in what context the function will be invoked.

function greet () {
alert(`Hello, my name is ${this.name}`)
}

const user = {
name: 'sachin',
age: 30,
}
greet.call(user);

It bounds the object to additional methods.

apply method
call and apply do the exact same things but in the apply method, we can pass a single array in it.

const languages = ['JavaScript', 'Java', 'C++']

// greet.call(user, languages[0], languages[1], languages[2])
greet.apply(user, languages)

bind method
It returns the method instance instead of value.

function greet (l1, l2, l3) {
alert(
`Hello, my name is ${this.name} and I know ${l1}, ${l2}, and ${l3}`
)
}
const user = {
name: ‘sachin’,
age: 30,
}
const languages = [‘JavaScript’, ‘Java’, ‘C++’];const newFn = greet.bind(user, languages[0], languages[1], languages[2])
newFn() // alerts “Hello, my name is sachin and I know JavaScript, Java, and C++”

Difference between call(),apply() and bind() —
1. Additional parameters
2.Bind() invokes later

10.Higher-order functions

A function that accepts and/or returns another function is called a higher-order function.

It is called higher because it operates on functions.
A Higher-Order function is a function that receives a function as an input argument or returns a function as output.
WHY ? —
* Fewer bugs
*Easy to Test
*Simple

Example:
Array.prototype.map, Array.prototype.filter and Array.prototype.reduce

//simple function
const arr1 = [1, 2, 3];
const arr2 = [];
for(let i = 0; i < arr1.length; i++) {
arr2.push(arr1[i] * 2);
zz}
console.log(arr2); //[ 2, 4, 6 ]//higher order function
const arr1 = [1, 2, 3];
const arr2 = arr1.map(function(item) {
return item * 2;
});
console.log(arr2);

11. Currying functions

It is a function that takes multiple arguments as input and returns the function with fewer parameters.

function multiply(x,y){
console.log(x*y)
}
multiply(5,4);
//Currying function
function multiply(x){
return function(y){
console.log(x*y);
}
}
multiply(2)(3); //6

12. Pure Functions

It is the function that always returns the same result if the same arguments are passed in it.

It does not produce any side effects.

function multiply(x,y){
console.log(x*y)
}
multiply(5,4);

In the above function, we can clearly see its uses arguments and returns output without modifying variables.
Benefits:
1.Easy to test
2.Simple

13. Debouncing and Throttling

Basically, there are two techniques use to improve the performance of the application and reduce backend hits.

Throttling:

Throttle prevents a function from executing more than once every specified amount of time, It also ensures that function is run regularly at a fixed rate.

if mentioned time is 200 milliseconds, no matter how many times the user hits the events in those milliseconds, the event only occurs once between that period.
consider this if user hits multiple events in 1 second then the function will get called only 5 times.

Debouncing:

Debouncing enforces that function not to be called again until a certain amount of time has passed without it being called. It is similar to throttle but in debouncing time delay is refreshed every time the event is triggered.

Consider the time delay is 200 milliseconds, and the event is triggered for 1 second, then the function will get a call after 1.05 seconds.

USE: Suppose you want updates from API on user click or interaction that time we can go for debouncing because timing is important here, and suppose we have to update UI on user interaction we can go for throttling

14. Deep Copy and Shallow Copy

Deep copying means that the value of the new variable is disconnected from the original variable while a shallow copy means that some values are still connected to the original variable.

Shallow copy:
Let's consider the below example:

var student = {
rollno: "11",
name: "sachin",
address: "mumbai",
marks: 80
}

console.log("student:", student);
var newStudent = student; // Shallow copy
console.log("New student:", newStudent);
newStudent.name = "virat";
console.log("student:", student);
console.log("New student:", newStudent);

if we see the result of the above code it returns the changes in the old created student object too, which means both are pointing to the same memory address even after we created a new one. so here e need a Deep copy to handle this issue.

Deep Copy:
Let's consider the below example:

var student = {
rollno: "11",
name: "sachin",
address: "mumbai",
marks: 80
}

console.log("student:", student);
var newStudent = JSON.parse(JSON.stringify(student); // Deep copy
console.log("New student=> ", newStudent);
newStudent.name = "virat";
console.log("student:", student);
console.log("New student=> ", newStudent);

In above code we use JSON.parse() and JSON.stringify() methods to make deep copy. and if see the result it will not affect the existing object, so it allocates new memory for it and affecting the existing object.

Deep copying can done by using three ways —

1.spread (…) syntax
2.Object.assign() method
3.JSON.stringify() and JSON.parse() methods

const student = {
firstName: 'sachin',
lastName: 'shinde'
};
// using spread ...
let s1 = {
...student
};
// using Object.assign() method
let s2 = Object.assign({}, student);
// using JSON
let s3 = JSON.parse(JSON.stringify(student));

15.Event Bubbling

Bubbling means an event propagates from the item that clicked up to all its parent tree.

let take the below example-

form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
//output
p
div
form

so the results will be p -> div -> form, this process is bubbling because events bubble from inner elements up through parents.

To stop event bubbling we can use event.stopPropagation() method.

<body onclick="alert(`the bubbling doesn't reach here`)">
<button onclick="event.stopPropagation()">Click me</button>
</body>

16.Event Capturing

It is the opposite of bubbling and events go down to target elements.

By default, the phase is bubbling to handle capturing phase you need to set the third optional arguments to true.

document.getElementById('container').addEventListener(
'click',
() => {
//window loaded
},
true
)

so here first all capturing events will occur after that all bubbling events will occur.

Event Cancellation: There are some events which are having default action, to stop that specific default action we can use e.preventDefault.

17.Function Expression and Function Declaration

Function Declaration: When we create a function with the name we can say it function declaration.

function getdata() {};

Function Expression: When we create a function and name may be omitted in function expression and it makes an anonymous function.

const getdata = function() {}
  • Function declaration loads before any code are executed while function expression load only when the execution reaches it.
  • A function declaration is hoisted but function expression is not

Function Expression Benefits:
1. Closures
2. Arguments to the function
3. IIFE

18. Use Strict

Basically, it is a new feature in ECMAScript 5. we use it at the top of javascript code. It gives a more restricted version of javascript. it looks like the string “use strict”.

"use strict"
a=1;
console.log(a);//output
ReferenceError: a is not defined

if we used “use strict” it will through error.

a=1;
console.log(a);//output
a

if we remove “use strict” it will return the result.

Strict mode does not allow you to delete a variable.
Duplicating parameter name is not allowed in this mode
It doesn't allow you to use a variable without declaring it.
Benefits:
1. Provide a secure way to write javascript.
2.Eliminates silent errors of javascript.

19. Identity Operator (===) vs. Equality Operator (==)

Equality Operator (==):

  • It is used for comparing two values but it ignores the data type of variable.
  • Returns true if two operands are equals

Identity Operator (===):

  • It is used for comparing two values and also check datatypes.
  • It returns true only if the data type and value are the same.
5 == "5" //true, Equality Operator (==)
5 === "5" //false, Identity Operator (===)

20. Generators

This is one of the best features introduced in ES6, a lesser-known but powerful feature. we already know to handle asynchronous we use callback, promises, async/await, but Generators also handle asynchronous code in a linear and straightforward fashion and provides pause and restart function abilities.

Basically, you can control the iterator by suspending and resuming it.

Syntax:

function* getdata() {
yield;
}

Example:

function* generator(i) {
yield i;
yield i + 10;
}
const gen = generator(10);console.log(gen.next().value);
// expected output: 10
console.log(gen.next().value);
// expected output: 20

21. Array Methods

An array having multiples methods for data operation. lets see in deatils —

  1. push(...items) : It adds items to the end of array.
let arr = [1, 2, 3, 4];
const newArry = arr.push(5); // [1, 2, 3, 4, 5]

2. pop(): It removes an item from the end of the array

let arr = [1, 2, 3, 4];
const popped = arr.pop(); // [1, 2, 3]

3. shift() – It removes an item from the beginning of the array.

let arr = [1, 2, 3, 4];
const shifted = arr.shift(); //[2, 3, 4]

4. unshift(...items): It adds items to the beginning.

let arr = [1, 2, 3, 4];
const unshifted = arr.unshift(5, 6, 7); //[5, 6, 7, 1, 2, 3, 4]

5. splice(pos, deleteCount, ...items) : at index pos deletes deleteCount elements and inserts items.

let arr = ['a', 'c', 'd', 'e'];
arr.splice(1, 0, 'b')

6. slice(start, end): It creates a new array, copies elements from index start till the end (not inclusive) into it.

let arr = ['a', 'b', 'c', 'd', 'e'];
const sliced = arr.slice(2, 4); //['a', 'b', 'c', 'd', 'e']

7. concat(...items): It returns a new array: copies all members of the current one and adds items to it. If any of the items is an array, then its elements are taken.

let arr = [1, 2];
const concatArray = arr.concat([3, 4]); // 1,2,3,4

8. indexOf/lastIndexOf(item, pos) : It look for item starting from position pos, return the index or -1 if not found.

const arr = ['Nick', 'Frank', 'Joe', 'Frank'];
const foundIndex = arr.indexOf('Frank'); //1

9. includes(value): It returns true if the array has value, otherwise false.

let arr = [1, 0];
const incArray = arr.includes(1); // true

10. find/filter(func) : It filter elements through the function, return first/all values that make it return true.

//find
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const found = arr.find(el => el > 5); //6
//filter
const arr = [1, 2, 3, 4, 5, 6];
const filtered = arr.filter(el => el === 2 || el === 4); //[2, 4]

11. findIndex: It is like find, but returns the index instead of a value.

const arr = ['Nick', 'Frank', 'Joe', 'Frank'];
const foundIndex = arr.findIndex(el => el === 'Frank'); //1

12. forEach(func) : It calls func for every element, does not return anything.

["sachin", "virat", "rohit"].forEach((item, index, array) => {
console.log(item);
});

12. map(func) : It creates a new array from the results of calling func for every element.

const arr = [1, 2, 3, 4, 5, 6];
const mapped = arr.map(el => el + 20); //[21, 22, 23, 24, 25, 26]

13. sort(func) : It sorts the array in-place, then returns it.

let arr = [1, 7, 3, -1, 5, 7, 2];
const sorter = (firstEl, secondEl) => firstEl - secondEl;
arr.sort(sorter); // [-1, 1, 2, 3, 5, 7, 7]

14. reverse() : It reverses the array in-place, then returns it.

let arr = [1, 2, 3, 4, 5];
arr.reverse(); // [5,4,3,2,1]

15. split/join : convert a string to array and back.

//splet str = "abc";
console.log( str.split('') ); // c,b,a
//joinlet arr = ['sachin', 'rohit', 'virat'];
let str = arr.join(';');
console.log( str ); // sachin;rohit;virat

16. reduce(func, initial) : It calculates a single value over the array by calling func for each element and passing an intermediate result between the calls.

const arr = [1, 2, 3, 4, 5, 6];
const reduced = arr.reduce((total,current) => total + current); //21

17. Array.isArray(arr): It checks arr for being an array.

console.log(Array.isArray({})); // false
console.log(Array.isArray([])); // true

22. Map and Set

ES6 introduced new data structures names as Map and Set.

Map:

The Map object allows you to store key-value pairs and remembers the original insertion order of the keys it means data can retrieved in the same order it was inserted.

//creating Map
const students = new Map(); //Map {}
//Adding items
students.set('sachin', {
address: 'Mumbai',
});
//output
Map(1) {"sachin" => {…}}
[[Entries]]
0: {"sachin" => Object}
size: (...)
__proto__: Map

Set:

It is a collection of unique values. Set also ordered collection of elements means elements get in the same order in which order they inserted.

const set = new Set(); //Set(0) {}//add item into set
set.add('sachin');
set.add('virat');
set.add('rohit');
Set(3) {"sachin", "virat", "rohit"}//unique values
const set = new Set(); //Set(0) {}
//add item into set
set.add('sachin');
set.add('virat');
set.add('sachin');
//output
Set(2) {"sachin", "virat"}

23. Closure

Now we are going to look into the most important concept of javascript is closure.

Let's take a simple example then we will go in deep.

function x(){
var a =26;
function y(){
console.log(a); // the code here has access anything declred in global scope
}
y();
}
x();
//output
26

This is what closure is! let's see in details

A function is bundled together with its lexical scope known as closure, so here function y is bind with function x variables.

So let's see MDN Definition :

A closure is a combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

function x() {
var a = 26;
function y() {
console.log(a);
}
return y; //its not only returning function its also return its lexical scope
}
var z = x();
console.log(z);
z();//26

Lexical Scope: The nested group of function and the inner function have access to the variables and other resources of their parent scope.

Example:

function grandFather(){
var grf = "I'm Grandfather";
console.log(grf);
function parent(){
var pr = "I'm Parent";
console.log(pr);
function child(){
var pr = "I'm Child";
console.log(pr);
}
return child;
}
return parent;
}
var x = grandFather();
console.log(x);
var z = x();
z();

Lets another example,

function car(){
var speed = 0;
return{
accelerate:function(){
speed++;
}
}
}
var car = new car();
car.accelerate();

In the above code speed is accessible due to closure, otherwise completely inaccessible.

Uses of Closures:
* Module Design Patterns
* SetTimeouts
* Currying

Thank for reading ✌️✌️✌️
Please claps and comments if you like !!👏👏👏

--

--

Prasad Sonawane

Love Programming and Writing, Software Engineer, Texas