Here I will share JavaScript Concepts from basic to advance.
Also, You can find conceptual questions
to practice your JavaScript
coding skills.
Click On ⭐ if you like this Repo. Pull Requests are highly appreciated. Follow me: ahsan-chy for more JS Topics update.
-
Data Types of JAvaScript
-
How javascript work???? 👩💻
-
🚀 Execution Context
- ⚡️ Global Execution Context (GEC)
- ⚡️Function Execution Context (FEC)
-
Execution Stack
-
Hoisting
-
Ground Rules of Hoisting
-
Type of Errors
- Reference Error:
- Syntax Error:
- Type Error:
-
- Lexical sope
- Variable Scope
- Scope Chain
- Garbadge Collecctor
-
Event Loops
-
Curring
-
Event Delegation
-
Event Propagation
- Event Bubbling
- Event Capturing
- Accessing the Target Element
- Event Propagation - StopPropagation
- Event Cancellation
- PreventDefault
- Immidiate propagation
-
Memoization
-
Denouncing
-
Throatling
Variable is just like container (storage area) to hold data.
A declaration of a variable is where a program says that it needs a variable.
int x, y, z; // declare 3 variables.
Initialization is the process of assigning a value to the Variable.
int marks = 90; // declare and initialize variable marks
var num = 60; // number
var user = "Fahad"; // string
var f = false; // boolean
var marks = [92,23,52,82]; // Array
var user = { n:1, N="Ali" }; // Object
let value = 0 / 0; // NaN
let name;
console.log(name); // undefined
Basically we have two main data types in JavaScript. Primitive
& NoN-Primitive
.
-
Primitive
- String
- Number
- Boolean
- Null
- Undefine
- Symbol
-
Non-Primitive
- Array
- Object Literls
- Functions
const user = {
firstName:"John",
lastName:"Doe",
age:50, eyeColor:"blue"
}
const u = user; // --- Shallow Copy of User
u.age = 10; // Will change both x.age and user.age
console.log(u.age) //Output: 10
console.log(user.age) //Output: 10
const products = [
{ id: 1, title: 'laptop' },
{ id: 2, title: 'Mobile' },
{ id: 3, title: 'Charger'},
{ id: 4, title: 'watch' },];
console.log(typeof JSON.stringify(products), JSON.stringify(products));
const person = '{"name":"John", "age":30, "city":"New York"}'
console.log(typeof JSON.parse(person), JSON.parse(person));
When the JavaScript engine executes a script for the first time, it creates the global
execution context. During this phase, the JavaScript engine performs the following tasks:
Create the global object i.e., window
in the web browser or global
in Node.js.
Create the this object and bind it to the global object.
- Global Execution Context (GEC)
- Function Execution Context (FEC)
.
-
The
Global Execution Context
is created when JAVASCRIPT start to run, and it represents Global Scope in JAVASCRIPT. -
Variables, and Function that is not inside any Function. A new
Execution Context
gets created every time a Function is executed. -
The Global Execution Context just like any other Execution Context, except that it gets created default. It is associated with
Global Object
. When means a window Object
this === window;
name == window.name;
- Before your javascript(.js) file run there is global execution context that is created even file is empty.Two phase Creation phase and Execution Phase.
- In creation phase GEC create global object and this. In browser global object will be browser.Javascript engines allocate memory for function even before your code run.
- After creation phase,There is Execution phase.
sayHi(); //hello
function sayHi() {
console.log("hello");
}
Javascript already know your function even before it is executed because of hoisting as in creation phase it memorized all javascript function declaration.
sayHi(); //error out because of sayHi is const varible not exact function.
const sayHi = function () {
console.log("hey");
};
Whenever a function is called, the JavaScript engine creates a different type of Execution Context known as a Function Execution Context (FEC) within the GEC to evaluate and execute the code within that function.
Execution Stack, also known as "Call Stack" is a stack with a LIFO(Last In First Out) structure, which is used to store all the Execution context created during code execution.
How the execution context is created
JavaScript Hoisting
refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, prior to execution of the code.
-
Before your javascript(.js) file run there is global execution context that is created even file is empty.Two phase Creation phase and Execution Phase.
-
In creation phase GEC create global object and this. In browser global object will be browser. Javascript engines allocate memory for function even before your code run.
-
After creation phase,There is Execution phase.
sayHi(); //hello
function sayHi() {
console.log("hello");
}
Javascript already know your function even before it is executed because of hoisting as in creation phase it memorized all javascript function declaration.
var a = 5;
function Square(a) {
return a * a;
}
var total = Square(a);
Hoisting only works for function declarations, not expressions. Here is an example of a function expression where the code execution will break.
getAge(1990);
var getAge = function (yearOfBirth) {
console.log(new Date().getFullYear - yearOfBirth);
};
.
In JavaScript, initializations are not hoisted.
// program to display value
console.log(a);
var a = 5;
Output
undefined;
.
Also, variable hoisting does not work for variables initialized with the let
or const
keyword. Trying to access a variable ahead of declaration and use the let and const keywords to declare it later will result in a ReferenceError.
// using test before declaring
console.log(name1);
.
Also, when the variable is used inside the function, the variable is hoisted only to the top of the function. For example,
// program to display value
var a = 4;
function greet() {
b = "hello";
console.log(b); // hello
var b;
}
greet(); // hello
console.log(b);
Output
hello
Uncaught ReferenceError: b is not defined
If a variable is used with the let keyword, that variable is not hoisted. For example,
// program to display value
console.log(num);
let num;
Output
Uncaught ReferenceError: Cannot access 'a' before initialization
.
In essence, a Reference Error occurs when JavaScript
- tries to access a variable that doesn't exist,
- hasn't been defined, or
- doesn't exist in the current scope from which you are trying to access it.
console.log(a);
let a = "apple";
// ReferenceError: Cannot access 'a' before initialization
function inScope() {
let a = "apple";
}
console.log(a);
// ReferenceError a is not defined.
console.log(a);
// ReferenceError a is not defined.
a = "apple";
console.log(a);
// apple,
In strict mode, though, JavaScript will catch this error and throw a reference error:
"use strict";
a = "apple";
console.log(a);
// ReferenceError: a is not defined
Syntax errors, also known as parse errors, occur in JavaScript when developers incorrectly use a predefined structure or language pattern.
const b; // uncaught Syntax Error:
let a = 100;
let a = 200; // uncaught Syntax Error:
Lots of Syntax errors occure in JS. You will see those in your JS journy.
An operand or argument passed to a function is incompatible with the type expected by that operator or function🙄
CONST b= 100;
b = 200; //uncaught TypeError: Assignment to consonent variable🙄
// type error
var a = "nemani";
console.log(a.reverse());
Temporal Dead Zone is the period of time during which the let
and const
declarations cannot be accessed.
let totalPrice = coursePrice * 1.18; //---------
// | ------> this is TDZ for coursePrice
// |
console.log(totalPrice); //---------
let coursePrice = 699;
- Clousers
- Lexical sope - Dynamic Scope
- Variable Scope
- Scope Chain
- Garbadge Collecctor
- Curring
Closure is a feature that allows a function to access variables from an outer function, even after the outer function has returned. This is possible because the inner function retains a reference to the environment in which it was created, including any variables or functions that were defined in that environment.
Javascript is the synchronous single-threaded language but with the help of event-loop and promises, JavaScript is used to do asynchronous programming.
function outerFunction() {
var outerVariable = "Hello, ";
function innerFunction(name) {
console.log(outerVariable + name);
}
return innerFunction;
}
var greeting = outerFunction();
greeting("John"); // Output: "Hello, John"
Some high-level languages, such as JavaScript, utilize a form of automatic memory management known as garbage collection (GC). The purpose of a garbage collector is to monitor memory allocation and determine when a block of allocated memory is no longer needed and reclaim it.
- Global Scope:
- Function Scope or Local Scope:
- Block Scope
A closure in JavaScript is a feature where an inner function has access to the outer (enclosing) function's variables — a scope chain. Closures in Javascript are created along with the function. Lexical scoping is the environment that holds the variables of the current scope as well as the outer scope.
Scope Chain means that one variable has a scope (it may be global or local/function or block scope) is used by another variable or function having another scope (may be global or local/function or block scope). This complete chain formation goes on and stops when the user wishes to stop it according to the requirement.
var x = 1; // Global scope
function outer() {
var y = 2; // Outer scope
function inner() {
var z = 3; // Inner scope
console.log(x + y + z); // Output: 6
}
inner();
}
outer();
Each time a new function is created in JavaScript, a new local scope is created for that function. This local scope is added to the scope chain and becomes the next outer scope for any nested functions.
Currying in JavaScript transforms a function with multiple arguments into a nested series of functions, each taking a single argument.
const add = (a, b, c) => {
return a + b + c;
};
console.log(add(2, 3, 5)); // 10
// Curried version
const addCurry = (a) => {
return (b) => {
return (c) => {
return a + b + c;
};
};
};
console.log(addCurry(2)(3)(5)); // 10
Why We use Curring:
- Currying helps you avoid passing the same variable multiple times
- Make higher order function
- There are three phases of event propagation:
- Capture Phase,
- Target Phase,
- Bubble Phase
Event Propagation concepts:
- Event Bubbling
- Event Capturing
- Accessing the Target Element
- Event Propagation - StopPropagation
- Event Cancellation
- PreventDefault
- Immidiate propagation
- The concept of event propagation describes how events propagate through the DOM tree to reach their destination and what happens to them once they do.
Detail Article About Event Propagation
- Event Bubbling is a concept in the DOM (Document Object Model). It happens when an element receives an event, and that event bubbles up (or you can say is transmitted or propagated) to its parent and ancestor elements in the DOM tree until it gets to the root element.
Important points about event bubbling:
- It only work if both inner and outer div/button have same events. Like child have onClick and parent have onClick then it will work. Otherwise it will not work.(One have click and other have hover event.)
Example:
<div id="outer">
<div id="middle">
<div id="inner">Click me</div>
</div>
</div>
const outer = document.getElementById("outer");
const middle = document.getElementById("middle");
const inner = document.getElementById("inner");
// Event bubbling
outer.addEventListener("click", () => console.log("Outer clicked"));
middle.addEventListener("click", () => console.log("Middle clicked"));
inner.addEventListener("click", () => console.log("Inner clicked"));
- The event is first captured by the outermost ancestor element (usually the root element), and then it traverses down the DOM hierarchy, passing through each descendant element down to the target element where the event originated.
- For the capturing phase, we need to set the handler capture option of addEventListener() to true.
Example:
<div id="outer">
<div id="middle">
<div id="inner">Click me</div>
</div>
</div>
const outer = document.getElementById("outer");
const middle = document.getElementById("middle");
const inner = document.getElementById("inner");
// Event capturing
outer.addEventListener("click", () => console.log("Outer capturing"), true);
middle.addEventListener("click", () => console.log("Middle capturing"), true);
inner.addEventListener("click", () => console.log("Inner capturing"), true);
-
StopPropagation method to stop the flow of an event through the DOM tree, but does not cancel the browser default behavior.
-
stops the upward movement
-
There are two techniques to stop the propagation: event.stopPropagation() as well as event.stopImmediatePropagation() .
function topDiv() {
alert("topDiv");
function middleDiv() {
event.stopPropagation(); //+ stopPropagation
alert("middleDiv");
function innerdiv() {
event.stopPropagation(); //+ stopPropagation
alert("innerdiv");
}
}
}
This time when I click on inner div it will call on inner don't move to upward.
- PreventDefault method to prevent the default behavior of an event, but does not stop the event flow.
Example 1:
<a href="https://www.example.com" id="link">
Click me
</a>
const link = document.getElementById('link');
link.addEventListener('click', function(event) {
// Prevent the default behavior of the link (navigating to the href URL)
event.preventDefault();
// Log a message to the console
console.log('Link clicked, but default action prevented.');
});
-
In this example, when you click on the link, the default behavior of the link (navigating to the URL specified in the href attribute) is prevented using event.preventDefault(). Instead, a message is logged to the console.
-
So, even though the link is clicked, the browser doesn't navigate to the URL specified in the href attribute because event.preventDefault() stops the default action associated with the event.
Example 2:
- Form Submition
-
Debouncing is a programming technique that helps to improve the performance of web applications by limiting the frequency of function calls.
-
🚀 It's a technique that optimizes performance by reducing unnecessary API calls.
Common usecase: Search Input
const debounce = (mainFunction, delay) => {
// Declare a variable called 'timer' to store the timer ID
let timer;
// Return an anonymous function that takes in any number of arguments
return function (...args) {
// Clear the previous timer to prevent the execution of 'mainFunction'
clearTimeout(timer);
// Set a new timer that will execute 'mainFunction' after the specified delay
timer = setTimeout(() => {
mainFunction(...args);
}, delay);
};
};
// Define a function called 'searchData' that logs a message to the console
function searchData() {
console.log("searchData executed");
}
// Create a new debounced version of the 'searchData' function with a delay of 3000 milliseconds (3 seconds)
const debouncedSearchData = debounce(searchData, 3000);
// Call the debounced version of 'searchData'
debouncedSearchData();
React Debouncing
This getData function, let's call our API.
useEffect(() => {
const getData = setTimeout(() => {
axios.get(`https://api.postalpincode.in/pincode/${pinCode}`).then((response) => {
console.log(response.data[0]);
});
}, 2000);
}, [pinCode]);
We will also need to destroy the instance of the useEffect hook using return, followed by clearTimeout, every time it finishes.
useEffect(() => {
const getData = setTimeout(() => {
axios.get(`https://api.postalpincode.in/pincode/${pinCode}`).then((response) => {
console.log(response.data[0]);
});
}, 2000);
return () => clearTimeout(getData);
}, [pinCode]);
- Throttling is a technique that limits how often a function can be called in a given period of time.
- It is useful for improving the performance and responsiveness of web pages that have event listeners that trigger heavy or expensive operations, such as animations, scrolling, resizing, fetching data, etc.
Why Use Throttling?
- Overloading the server or the browser with too many requests or calculations
- It returns a new function that, when called, ensures that func is not invoked more frequently than delay milliseconds.
- Exceeding the rate limits or quotas of APIs or services
Example: We set button disabled until the form data submited to server.
function throttle(mainFunction, delay) {
let timerFlag = null; // Variable to keep track of the timer
// Returning a throttled version
return (...args) => {
if (timerFlag === null) {
// If there is no timer currently running
mainFunction(...args); // Execute the main function
timerFlag = setTimeout(() => {
// Set a timer to clear the timerFlag after the specified delay
timerFlag = null; // Clear the timerFlag to allow the main function to be executed again
}, delay);
}
};
}
// Define a function that fetches some data from an API
function fetchData() {
console.log("Fetching data...");
// Simulate an API call with a random delay
setTimeout(() => {
console.log("Data fetched!");
}, Math.random() * 1000);
}
// Throttle the fetchData function with a delay of 5000 ms
const throttledFetchData = throttle(fetchData, 5000);
// Add an event listener to the window scroll event that calls the throttledFetchData function
window.addEventListener("scroll", throttledFetchData);
Memoization is a technique used in computer programming to optimize the performance of functions by caching their results. It involves storing the results of expensive function calls and retrieving them from a cache when the same inputs are encountered again.
The idea behind memoization is that if a function is called with the same arguments multiple times, it's more efficient to compute the result once and store it for future use, rather than recomputing it every time. This can significantly improve the performance of algorithms that have overlapping subproblems or repetitive computations.
Here is an example of how memoization can be used to speed up a recursive function:
function factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)
This function is recursive, and it calls itself n times when n is greater than 0. This can make the function very slow for large values of n.
const memo = {};
function factorialMemoized(n) {
if (n in memo) {
return memo[n];
}
else {
const result = n * factorialMemoized(n - 1);
memo[n] = result;
return result;
}
This code uses a hash table to store the results of factorial calls. When factorial is called, the hash table is checked to see if the nth factorial has already been computed. If it has, the result is returned from the hash table. If not, the factorial is computed and stored in the hash table.
This code can significantly speed up the factorial function. For example, factorial(100) takes about 100 milliseconds to compute without memoization, but it takes only about 1 millisecond to compute with memoization.
In JavaScript, the event loop is a mechanism that handles asynchronous operations. It is an essential part of the language's concurrency model, which allows the program to perform multiple tasks concurrently without blocking the execution of other code.
In JavaScript, the event loop is responsible for handling several types of asynchronous operations, including:
Timers: JavaScript provides two types of timers, setInterval() and setTimeout(), which allow the programmer to schedule code to run after a certain amount of time has passed.
User Interface (UI) events: These are events that are triggered by user interaction with the page, such as mouse clicks, keypresses, and form submissions.
Network requests: JavaScript code can send HTTP requests to servers to retrieve data. Since network requests can take a long time to complete, they are typically handled asynchronously to avoid blocking the program's execution.
File I/O operations: JavaScript can read and write files from the local file system. These operations can take a significant amount of time, so they are usually performed asynchronously.
Promises: Promises are a mechanism for handling asynchronous operations in JavaScript. They represent a value that may not be available yet, but will be at some point in the future. When the value is available, the promise is fulfilled, and the associated code can be executed.
console.log("Starting...");
setTimeout(function () {
console.log("Timer done!");
}, 2000);
console.log("Waiting for timer...");
Output:
Starting...
Waiting for timer...
Timer done!