Author: Saim Khalid

  • SessionStorage

    Session Storage in JavaScript

    The JavaScript sessionStorage object is a window object property that appears in all current browsers. The page’s protocol, host-name and port are linked to any sessionStorage data. Each window contains its own session storage. The JavaScript sessionStorage method contributes to the reliable storage of user data.

    In JavaScript, we use the getItem() method to get elements that are always stored in the session storage attribute. The storage object includes a getItem() method. It could be a JavaScript session Storage object or a local Storage object.

    Syntax

    Below is the syntax of the JavaScript SessionStorage −

    window.sessionStorage;

    Methods of SessionStorage

    To get an element from JavaScript sessionStorage we have to create the element and save it to session storage. We can get it back later. The storage object has four methods: setItem(), getItem(), removeItem(), key() and clean().

    setItem()

    Used to save information in a session storage item. Here is how you can use it in your code −

    sessionStorage.setItem("key","value");

    getItem()

    Used to retrieve and display a session storage item. Following is way you can use it in your code −

    let lastname = sessionStorage.getItem("key");

    removeItem()

    Used to delete a specific item from session storage. The following is how you can utilize it in your code −

    sessionStorage.removeItem("key");

    key()

    Used to get the name of a data item in session storage. Here is how you can utilize it in your code −

    let keyName = sessionStorage.key(index);

    clear()

    Used to clear all items in session storage. Here’s how you can put it into your code −

    sessionStorage.clear();

    Why do use sessionStorage in JavaScript

    SessionStorage can be used in many different ways. And these are the most important ones:

    • The web-page interface can be saved in session storage.
    • When the user returns to the page later, you can use sessionStorage to restore the previously saves user interface.
    • SessionStorage can also be used to transmit data between pages instead of using hidden input fields or URL parameters.

    Key Features

    Below are some key features of JavaScript’s SessionStorage −

    • Temporary Storage: Data is only stored for the duration of the session.
    • Tab-specific: Data is only accessible in the same browser tab or window.
    • Simple API: Provides simple methods for configuring, retrieving, and removing items.
    • Persistent During Reloads: Data is preserved after a page reload within the same session.
    • Storage Limit: Typically gives about 5MB of storage per origin for session data.

    Examples

    Following are some examples of the session storage −

    Example 1

    The example below shows how to use JavaScript sessionStorage with the set and get items methods. A session storage object helps us to create arrays and set data. After that, display information by calling the object’s getItem() function.

    <html><head><title>Display Data Using sessionStorage in JavaScript</title></head><body style="text-align: center;"><h4>Display Data Using sessionStorage in JavaScript</h4><p id="displayStudent"></p><p id="displayTutorial"></p><script>  
       const studentInfo = {  
    
      name: 'Amit',  
      rollNumber: 41,  
      website: 'Tutorialspoint',  
      learningMode: 'online',  
      age: 20,  
      subject: 'JavaScript'  
    }; const tutorialInfo = {
      subject: 'JavaScript',  
      website: 'Tutorialspoint',  
      mode: 'online',  
      cost: 'Free',  
      targetAudience: 'students and beginners'  
    }; // Set and display studentInfo in sessionStorage sessionStorage.setItem("studentInfo", JSON.stringify(studentInfo)); console.log("Student info saved in sessionStorage."); let retrievedStudent = sessionStorage.getItem('studentInfo'); document.getElementById('displayStudent').innerHTML = retrievedStudent; // Set and display tutorialInfo in sessionStorage sessionStorage.setItem("tutorialInfo", JSON.stringify(tutorialInfo)); console.log("Tutorial info saved in sessionStorage."); let retrievedTutorial = sessionStorage.getItem('tutorialInfo'); document.getElementById('displayTutorial').innerHTML = retrievedTutorial; </script></body></html>

    Output

    This will generate the below result −

    Display Data Using sessionStorage in JavaScript
    
    {"name":"Amit","rollNumber":37,"website":"Tutorialspoint","learningMode":"online","age":19,"subject":"JavaScript"}
    
    {"subject":"JavaScript","website":"Tutorialspoint","mode":"online","cost":"Free","targetAudience":"students and beginners"}
    

    Example 2

    This example shows how to use JavaScript session storage with the set and get items methods. To display the date and time, we can use the object’s getItem() method. The array value use the get and set methods, whereas the date value writes data immediately.

    <html><head><title> Display Data Using sessionStorage in JavaScript </title></head><body style="text-align: center;"><h4> Display Data Using sessionStorage in JavaScript </h4><p id="displayStudentInfo"></p><p id="displayCurrentTime"></p><script>  
       const learnerDetails = {  
    
      name: 'Amit',  
      rollNumber: 52,  
      website: 'TutorialsPoint',  
      learningMode: 'online mode',  
      age: 22,  
      subject: 'JavaScript'  
    }; // Store student info in sessionStorage sessionStorage.setItem("learnerDetails", JSON.stringify(learnerDetails)); console.log("Learner details saved in sessionStorage."); // Retrieve and display student info let retrievedStudentInfo = sessionStorage.getItem('learnerDetails'); document.getElementById('displayStudentInfo').innerHTML = retrievedStudentInfo; // Store and display current date and time let currentDateTime = new Date(); sessionStorage.currentDateTime = currentDateTime; console.log("Current date and time saved in sessionStorage."); let retrievedDateTime = sessionStorage.getItem('currentDateTime'); document.getElementById('displayCurrentTime').innerHTML = retrievedDateTime; </script></body></html>

    Output

    This will lead to the following result −

    Display Data Using sessionStorage in JavaScript
    {"name":"Amit","rollNumber":52,"website":"TutorialsPoint","learningMode":"online mode","age":22,"subject":"JavaScript"}
    Fri Oct 25 2024 13:14:10 GMT+0530 (India Standard Time)
    

    Example 3

    Here is an example of using sessionStorage in an HTML file with JavaScript. The sessionStorage is used to save data for the life of the session and makes it available as long as the browser tab is open.

    <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Session Storage Example</title></head><body><h1>Session Storage Demo</h1><label for="name">Enter your name:</label><input type="text" id="name" /><button onclick="saveName()">Save Name</button><button onclick="showName()">Show Name</button><button onclick="clearName()">Clear Name</button><p id="result"></p><script>
       // Function to save name in sessionStorage
       function saveName() {
    
      let name = document.getElementById('name').value;
      sessionStorage.setItem('userName', name);
      document.getElementById('result').innerText = 'Name saved in session storage!';
    } // Function to show name from sessionStorage function showName() {
      let name = sessionStorage.getItem('userName');
      if (name) {
         document.getElementById('result').innerText = 'Stored Name: ' + name;
      } else {
         document.getElementById('result').innerText = 'No name found in session storage!';
      }
    } // Function to clear name from sessionStorage function clearName() {
      sessionStorage.removeItem('userName');
      document.getElementById('result').innerText = 'Name cleared from session storage!';
    } </script></body></html>

    Output

    So you have to open the file in a browser. Then enter a name and then click Save Name. After that you have to click Show Name to see the stored name. And click Clear Name to remove it from storage.

    Javascript SessionStorage

    Summary

    The sessionStorage allows you to store data just for a single session. When you close a browser tab or window, the sessionStorage data is automatically removed. As the sessionStorage is a Storage system object, you can handle its data using Storage-specific methods.

  • Selection API

    JavaScript Selection API allows us to access and change or modify the portion of a web page which is selected by the user.This includes selecting which text or elements to highlight and providing options for connecting with the selected item. But it should be noticed that the Selection API is not available in Web Workers so it can only be used in a web-page’s main thread.

    The JavaScript Selection API provides a variety of essential APIs that allow developers to access and modify certain portions of a document. These interfaces contain the Selection and Range objects, as well as a number of helpful events that occur all over the selection process.

    Selection Interface

    A Selection object shows the user-selected region of text or the cursor’s current position. Each document is given a unique selection object, which can be retrieved via document.getSelection(), or Window.getSelection() can then be examined and modified.

    A user can choose between left to right and right to left. The anchor is where the user began the selection, whereas the focus is where the user ends it. When you make a selection with a desktop mouse, the anchor is set where you pressed the mouse button and the focus is placed where you let go.

    Instance Properties

    Here is the table provided of instance properties you can use for you reference −

    PropertyDescription
    Selection.anchorNodeThis shows the part of the document where the selection starts. It will be empty if nothing was selected.
    Selection.anchorOffsetThis tells how far the starting point of the selection is from the beginning. If the selection starts in a piece of text, it shows the number of characters before it. If it starts in an element (like a paragraph or list), it shows the number of other elements before it.
    Selection.directionThis shows if the selection was made from left to right, or right to left.
    Selection.focusNodeThis shows the part of the document where the selection ends. It can also be empty if nothing was selected.
    Selection.focusOffsetSimilar to anchorOffset, this shows how far the ending point of the selection is from the beginning in the focusNode.
    Selection.isCollapsedThis is true if the selection has no length (meaning the start and end are the same point), and false if there is a range of selected text.
    Selection.rangeCountThis tells you how many separate selections are made.
    Selection.typeThis gives the type of selection. It can be “None” (no selection), “Caret” (a single point), or “Range” (a highlighted area).

    Instance Methods

    Here is the table below in which we have provided the list of instance methods of Selection Interface with its description.

    MethodsContent
    Selection.addRange()A Range object that will be added to the selection.
    Selection.collapse()Collapses the current selection to a single point.
    Selection.collapseToEnd()Collapses the selection to the end of the last range in the selection.
    Selection.collapseToStart()Collapses the selection to the start of the first range in the selection.
    Selection.containsNode()Indicates if a certain node is part of the selection.
    Selection.deleteFromDocument()Deletes the selection’s content from the document.
    Selection.empty()Removes all ranges from the selection, leaving the anchorNode and focusNode properties equal to null and nothing selected.
    Selection.extend()Moves the focus of the selection to a specified point.
    Selection.getComposedRanges() ExperimentalReturns an array of StaticRange objects, each that represents a selection that might cross shadow DOM boundaries.
    Selection.getRangeAt()Returns a Range object representing one of the ranges currently selected.
    Selection.modify()Changes the current selection.
    Selection.removeRange()Removes a range from the selection.
    Selection.removeAllRanges()Removes all ranges from the selection.
    Selection.selectAllChildren()Adds all the children of the specified node to the selection.
    Selection.setBaseAndExtent()Sets the selection to be a range including all or parts of two specified DOM nodes, and any content located between them.
    Selection.setPosition()Collapses the current selection to a single point.
    Selection.toString()Returns a string currently being represented by the selection object, i.e., the currently selected text.

    Document.getSelection()

    The getSelection() method of the Document interface returns the Selection object for this document which represents the user-selected text range or the caret’s current position. And it acts like the window.getSelection().

    Syntax

    Below is the syntax of the Document.getSelection() −

     document.getSelection()

    Parameters

    This method works without the need for any parameters.

    Return value

    This method basically returns a Selection object, or null if there is no browsing context (e.g., unattached <iframe>).

    Example

    Following is the example for showing the usage of Document.getSelection() object −

    <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document.getSelection Example</title></head><body><p>Select some text in this paragraph and check the console.</p><button onclick="getSelectedText()">Get Selected Text</button><script>
    
      function getSelectedText() {
         let selection = document.getSelection();
         if (selection.rangeCount &gt; 0) {
            document.write("Selected text: " + selection.toString());
         } else {
            document.write("No text selected.");
         }
      }
    </script></body></html>

    Output

    To view the messages use the browser’s developer tools (usually by pressing F12) and select the “Console” tab.

    Check the below message on the console after running the above code in browser −

    Selected text: paragraph and check
    

    Window.getSelection()

    The getSelection() method of the Window interface returns the Selection object connected with the window’s document which represents the range of text selected by the user or the caret’s current position.

    Syntax

    Below is the syntax of the Window.getSelection() −

     window.getSelection()

    Parameters

    This method does not require any parameters.

    Return value

    This method returns a Selection object or null if the connected document has browsing context (e.g. if the window is an unattached <iframe>).

    Firefox gives null when a <iframe> is not displayed (e.g., display: none), whereas other browsers provide a Selection object with Selection.type set to None.

    Example

    Here is the example for showing the usage of Window.getSelection() object −

    <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Window.getSelection Example</title></head><body><p>Select some text in this paragraph and check the console.</p><button onclick="getSelectedText()">Get Selected Text</button><script>
    
      function getSelectedText() {
         let selection = window.getSelection();
         if (selection.rangeCount &gt; 0) {
            document.write("Selected text: " + selection.toString());
         } else {
            document.write("No text selected.");
         }
      }
    </script></body></html>

    Output

    So when to run the above HTML code in your browser. There will be a text will be appear with a button “Get Selected Text”. So when you select some text and press the button then after inspecting the element you can see the below message on the console −

    Selected text: this paragraph
    

    Document: selectionchange event

    The Selection API’s selectionchange event is triggered when a Document’s current Selection is changed. This event cannot be canceled and will not bubble.

    The event can be handled by registering an event listener for selectionchange or by using the onselectionchange event handler.

    Syntax

    Use the event name in methods like addEventListener() or to set an event handler property.

    addEventListener("selectionchange",(event)=>{});onselectionchange=(event)=>{};

    Event type

    It is a generic Event.

    Example

    Here is the example of selectionchange event −

    <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Selection Change Example</title></head><body><h1>Text Selection Example</h1><p>Select some text in this paragraph to see the console messages about selection changes.</p><p>Try selecting different parts of this text to see how it updates!</p><script>
    
      // Using addEventListener to track selection changes
      document.addEventListener("selectionchange", () =&gt; {
         document.write("Selection has changed:", document.getSelection().toString());
      });
        
      // Using onselectionchange to handle selection changes
      document.onselectionchange = () =&gt; {
         document.write("Updated selection:", document.getSelection().toString());
      };
    </script></body></html>

    Output

    When you select any text in these paragraphs it will send messages to the console telling you that your selection has changed and displays the currently selected material.

    [Log] Selection has changed: - "" (example.html, line 16)
    [Log] Updated selection: - "" (example.html, line 21)
    [Log] Selection has changed: - "" (example.html, line 16)
    [Log] Updated selection: - "" (example.html, line 21)
    [Log] Selection has changed: - "" (example.html, line 16)
    [Log] Updated selection: - "" (example.html, line 21)
    [Log] Selection has changed: - "Selection" (example.html, line 16)
    [Log] Updated selection: - "Selection" (example.html, line 21)
    

    Node: selectstart event

    This event occurs when the user starts making a new selection. For example, they can click and drag to highlight text. Suppose the event has been canceled so the selection is not changed.

    Syntax

    Use the event name in methods such as addEventListener() or set an event handler property.

    addEventListener("selectstart",(event)=>{});onselectstart=(event)=>{};

    Event type

    It is also a generic Event.

    Example

    Here is the example of selectstart event −

    <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Select Start Example</title></head><body><h1>Text Selection Example</h1><p>Choose some text in this paragraph to check the console messages.</p><p>Try different parts of this text!</p><script>
    
      // Using addEventListener to track when text selection starts
      document.addEventListener("selectstart", () =&gt; {
         document.write("Selection started");
      });
      
      // Using onselectstart to handle when text selection starts
      document.onselectstart = () =&gt; {
         document.write("Selection started.");
      };
    </script></body></html>

    Output

    When you choose any text in these paragraphs, it will display “Selection started” and “Selection started.” on the terminal.

    [Log] Selection started (example.html, line 16)
    [Log] Selection started. (example.html, line 21)
    [Log] Selection started (example.html, line 16)
    [Log] Selection started. (example.html, line 21)
    [Log] Selection started (example.html, line 16)
    [Log] Selection started. (example.html, line 21)
    [Log] Selection started (example.html, line 16)
    [Log] Selection started. (example.html, line 21)
    [Log] Selection started (example.html, line 16)
    [Log] Selection started. (example.html, line 21)
  • Require() Function

    Files with JavaScript code that performs a specific task are called modules. It is easy to add, remove, and update functionalities without affecting your entire code because modules are self-contained and separated from other parts of code.

    If these modules are in separate JavaScript files you should use them inside the original JavaScript code.

    In this chapter we will cover what the require() function does, how it can be used and some differences between the require and import functions.

    How to Run ES and CommonJS Modules

    JavaScript module execution in the browser is dependent on import and export statements. The ES modules are loaded and exported by these statements, respectively. Most of online browsers support it by default and it is the accepted and official method for reusing modules in JavaScript.

    By default, Node.js supports the CommonJS module format which uses module.exports to export modules and require() to import them.

    JavaScript require() Function

    The require() method, a built-in CommonJS module function that Node.js supports, is how you add modules to your project. The reason for this is that by default, Node.js treats JavaScript code as CommonJS modules.

    The require() function accepts a string parameter that specifies the module’s path. This could be −

    • Core module: Node.js has various built-in modules. When the require() function receives a string parameter that does not begin with “./”, “../”, or “/”, it assumes it is a core module or a module installed in the node_modules folder.
    • Local Module: If the argument begins with “./” or “../”, Node.js treats it as a relative or absolute path and is trying to load the module from a file in that location.
    • Installed module: Node.js modules can also be installed via npm (Node Package Manager). In this case, Node.js searches for the module in the node_modules folder.

    Usage of require() Function

    The require() method is easy to use and understand because all you have to do is assign it to a variable. This function will accept the location name as an input. The following is the general syntax −

    const locName =require(locationName);

    Let us suppose you have a CommonJS module that exports the function *get_full_name*, as seen below −

    //utils.jsconstget_full_name=(first_name, last_name)=>{returnMy full name is ${first_name} ${last_name};};
    module.exports = get_full_name;

    This module can then be used or included in your JavaScript file using the require() function −

    //index.jsconst get_full_name =require('./utils.js');
    console.log(get_full_name('Amit','Sharma'));

    The module is placed within a local file in the above code, so the local address is referred by the file name. But if you want to add an external module from the web you can use the web-based location −

    const myVar =require('http://web-module.location');

    Difference Between require() & import() Functions

    The require() and import() commands both provide code from external files into your JavaScript program, but they work differently. Here are the two main differences −

    • You can use the require() method throughout your code. You can even use it conditionally which means it will only run under certain conditions.
    • The import statement is always executed at the start of the file and cannot be used conditionally.
    • If you want to use require(), the file you are importing must have the .js extension.
    • If you are using an import, the file’s extension should be .mjs.

    The require() function is more flexible and can be used throughout your code, but import is more limiting, always starting at the beginning and frequently needing different file extensions.

  • Reactivity

    In web development responsiveness refers to how a system responds to changes in data. This concept is important for creating modern, dynamic web applications in which user actions need speedy responses like updating a page without reloading it or dynamically changing what the user sees when data is updated.

    This chapter covers some powerful JavaScript patterns that can help you build reactive systems. These patterns are commonly used in frameworks like React and Vue but understanding them in simple JavaScript allows you to understand the fundamental concepts.

    Why use Reactivity?

    Web apps today are highly interactive. When users click buttons, navigate pages or enter data so the application must reply quickly and change what shows on the screen. Addressing such updates manually can be difficult, but reactivity makes it easier by automatically responding to changes in data.

    Key Reactivity Patterns in JavaScript

    Now we will explore some key reactivity patterns in JavaScript in this section. These patterns allow us to react rapidly to changes in data and how the page responds to user actions. They are used in many popular libraries and frameworks but we will show how to use them in simple JavaScript.

    Pub/Sub: Simplified Data Flow Management

    The Pub/Sub (Publisher/Subscriber) pattern is a common approach to manage data flows. It differentiates between code that changes data (publisher) and code that responds to those updates (subscriber). This makes it easy to manage the various parts of an app separately.

    Example

    Following is the simple demonstration of this Reactivity Pattern −

    classPS{constructor(){this.t ={};// Tracks events and subscribers}sub(tp, cb){if(!this.t[tp]){this.t[tp]=[];}this.t[tp].push(cb);}pub(tp, d){if(this.t[tp]){this.t[tp].forEach((cb)=>cb(d));}}}const ps =newPS();
    ps.sub('n',(msg)=> console.log(News: ${msg}));
    ps.pub('n','New update available!');

    This method is used in systems like Redux in which components notice changes and update accordingly.

    Output

    This will generate the below result −

    News: New update available!
    

    Custom Events: Browser-Built Pub/Sub for Reactivity

    The browser provides an API for triggering and subscribing to custom events using the CustomEvent class and dispatchEvent method. The latter allows us to not only create an event, but also attach any needed data to it.

    Example

    Below is a simple example of this reactivity pattern −

    const evt =newCustomEvent('cEvt',{
      detail:'Event data',});const el = document.getElementById('target');
    el.addEventListener('cEvt',(e)=> console.log(Event: ${e.detail}));
    el.dispatchEvent(evt);

    This technique is appropriate for simple event triggering without the need of libraries.

    Output

    This will give the following outcome −

    Event: Event data
    

    Custom Event Targets

    If you do not wish to dispatch events globally on the window object, you can create your own event target.

    By modifying the native EventTarget class you can transmit events to a new instance. This makes sure your events are only triggered for the new class which prevents global propagation. Also you can connect handlers directly to this instance.

    Example

    The following code shows a simple use of this reactivity pattern −

    classCETextendsEventTarget{trig(evtName, evtData){const evt =newCustomEvent(evtName,{ detail: evtData });this.dispatchEvent(evt);}}const ct =newCET();
    ct.addEventListener('cEvt',(e)=> console.log(Event: ${e.detail}));
    ct.trig('cEvt','Hello from event!');

    Output

    This will create the below outcome −

    Event: Hello from event!
    

    Observer Pattern: Flexible Updates for Decoupled Code

    The Observer pattern is very similar to PubSub. You can subscribe to the Subject, which informs its subscribers (Observers) of changes and allows them to reply accordingly. This method is important for designing a disconnected and flexible architecture.

    Example

    Here’s a basic demonstration of how this reactivity pattern works −

    classSub{constructor(){this.obs =[];}sub(o){this.obs.push(o);}notify(d){this.obs.forEach((o)=> o.up(d));}}classObs{constructor(n){this.n = n;}up(d){
    
    console.log(${this.n} got: ${d});}}const s =newSub();const o1 =newObs('Obs 1');
    s.sub(o1); s.notify('New info!');

    Output

    This will create the below outcome −

    Obs 1 got: New info!
    

    Reactive Properties with Proxy

    The Proxy object lets you intercept property access actions (get and set) within objects. This enables you to create reactivity and run code whenever a property’s value is retrieved or updated.

    Example

    This is a simple example showing the reactivity pattern in action −

    let d ={ n:'Akash', a:25};let rData =newProxy(d,{get(t, p){
    
    console.log(Read: "${p}" = ${t[p]});return t[p];},set(t, p, v){
    console.log(Changed: "${p}" from ${t[p]} to ${v});
    t[p]= v;returntrue;}});
    rData.n ='Vikas';

    Output

    This will lead to the following outcome −

    Changed: "n" from Akash to Vikas
    

    Individual Property Reactivity

    If you don’t need to keep track of all an object’s fields you can use Object.defineProperty to select one or Object.defineProperties to group them together.

    Example

    The code below provides a clear demonstration of this reactivity pattern −

    let u ={ _n:'Akash'};
    
    Object.defineProperty(u,'n',{get(){returnthis._n;},set(v){
    
    console.log(Name changed from ${this._n} to ${v});this._n = v;}});
    u.n ='Vikas';

    Output

    This will produce the following result −

    Name changed from Akash to Vikas
    

    Reactive HTML Attributes with MutationObserver

    MutationObserver is a JavaScript API that monitors changes to the DOM (Document Object Model) like attribute changes, element additions or deletions etc. It is particularly applicable for running code in response to DOM changes without having to actively monitor them.

    Example

    Here is a simple implementation of this particular reactivity pattern −

    const el = document.getElementById('obs-el');const obs =newMutationObserver((muts)=>{
      muts.forEach(m=>{if(m.attributeName ==='class'){
    
      console.log('Class changed!');}});});
    obs.observe(el,{ attributes:true});

    Output

    This will generate the below result −

    Class changed!
    

    Reactive Scrolling with IntersectionObserver

    The IntersectionObserver API allows you to asynchronously monitor changes in the intersection of a target element with an ancestor element or the viewport of a top-level document. This means you can execute code anytime an element enters or quits the viewport or another specific element.

    This is particularly useful for lazy image loading, infinite scrolling, scroll position-based animations, and other features.

    Example

    Below is an example of this reactivity pattern −

    // Function to be called when the observed element is intersectingfunctiononIntersect(entries, obs){
      entries.forEach(e=>{
    
    e.target.style.backgroundColor = e.isIntersecting ?'lightgreen':'grey';});}// Create an IntersectionObserver with the callbackconst obs =newIntersectionObserver(onIntersect);// Start observing an elementconst el = document.getElementById('spec-el');
    obs.observe(el);

    Output

    Below is the output when the element enters the viewport −

    Element is now in view.
    

    This will be the output when the element exits the viewport −

    Element is now out of view.

  • Prototypal Inheritance

    Prototypal Inheritance

    The classical inheritance phenomenon involves making a new class that actually extends or reuses the attributes, functions, or methods of another class that are used by different programming languages (like C, C++, Java, and so on). JavaScript uses Prototype Inheritance rather than classical inheritance.

    Prototype Inheritance happens when one object uses the prototype linkage to access another object’s attributes or methods. All JavaScript objects derive properties and methods from their prototypes. Prototypes are hidden objects that inherit properties and methods from their parent classes.

    Syntax

    The prototype inheritance syntax contains the __proto__ property, which allows access to the child prototype. The syntax for prototype inheritance is as follows −

     child.__proto__ = parent;

    Example of Prototypal Inheritance

    Assume you have a basic car (a “prototype”) with basic parts like wheels and an engine. Now you want to create a certain vehicle, like a sports car, without beginning from scratch. Instead of creating a new vehicle, you use the existing one as a foundation (prototype). The sports vehicle inherits the basic car’s features (wheels and engine) but it can also be upgraded with optional features like more horsepower or a spoiler.

    It works the same way in JavaScript. An object can inherit characteristics and methods from another object (its prototype); still the inherited object can also be extended.

    Code for the Example

    In this case, sportsCar inherits the car’s characteristics and methods while also including a new feature called speed.

    // Basic car object (prototype)let car ={
       wheels:4,drive:function(){
    
     console.log("The car is driving");}};// SportsCar objectlet sportsCar ={
    speed:200,};// SportsCar object inherits from car object sportsCar.__proto__ = car; console.log(sportsCar);// Calling method from car object using sportsCar sportsCar.drive();// Inherited from car console.log(sportsCar.wheels);// New property in sportsCar console.log(sportsCar.speed);

    Output

    Here is the outcome of the above code −

    The car is driving
    4
    200
    

    We can now use any method and property from the ‘car’ object to the ‘sportsCar’ object. Later in the chapter, we will talk about how an object’s characteristics and methods are inherited.

    Properties of Prototypal Inheritance

    Here are some of the properties of Prototypal inheritance is listed −

    • Properties are the characteristics that identify an object, such as its status or features.
    • In the case of prototypical inheritance, the prototype’s properties are shared by all instances that inherit from it.
    • When a property is accessed on an object, JavaScript searches for it first in the object and then in its prototype chain.
    • Properties can include data values, arrays, objects or other JavaScript data types.
    • Properties may be data values, arrays, objects, or any other JavaScript data type.

    Methods of Prototypal Inheritance

    Methods are functions that are bound to an object and can perform operations or computations on its data. Prototypical inheritance allows all instances that inherit from a prototype to share the methods provided in it. Methods can be invoked on an object, giving access to its attributes and other methods. It can execute a variety of tasks, such as modifying data, making calculations, and communicating with other objects or the environment.

    Now let us discuss some methods of prototypal inheritance in the below section −

    Object.create

    Object.create is a JavaScript method that generates a new object from the prototype and its properties. It enables you to create an object that inherits from a prototype without requiring a constructor method. This function is commonly used to initialize the prototype chain for objects, so it allows prototype-based inheritance.

    The syntax for Object.create is provided below −

     Object.create(proto,[propertiesObject]);

    Here,

    • – proto is the prototype object that the newly formed object will inherit from.
    • – propertiesObject (optional) is an object that defines additional properties for the newly created object. These properties are added to the newly created object and replace properties with the same name from the prototype chain.

    Example

    Here is an example for showing how to use Object.create method −

    // Create a prototype objectconst animalPrototype ={describe:function(){
    
    console.log(This is a ${this.species}, and it is ${this.age} years old.);}};// Create a new object that inherits from the animalPrototypeconst tiger = Object.create(animalPrototype);
    tiger.species ='Tiger'; tiger.age =5;// Call the describe method on the tiger object tiger.describe();
    Output

    This will generate the below result −

    This is a Tiger, and it is 5 years old.
    

    Object.prototype.constructor

    The property Object.prototype.constructor denotes the function that produced the object instance. When an object is created with a constructor function or class, the constructor attribute is immediately set to the prototype.

    The constructor property is widely used to validate an object’s type and create new instances of the same type via JavaScript’s prototype-based inheritance.

    Example

    Here is an example of how to use Object.prototype.constructor −

    // Constructor function for creating Animal objectsfunctionAnimal(species, age){this.species = species;this.age = age;}// Creating an instance of the Animal objectconst animal =newAnimal('Tiger',5);// Accessing the constructor property of the prototype
    console.log(animal.constructor);
    Output

    This will produce the following result −

     Animal(species, age) { this.species = species; this.age = age; }
    

    hasOwnProperty

    In JavaScript the hasOwnProperty function is used to determine whether an object has a given property rather than inheriting it from its prototype chain. It returns a boolean value indicating whether the object contains the given property.

    The hasOwnProperty function is widely used in JavaScript to differentiate between an object’s own properties and those inherited from its prototype chain to make sure the property being checked exists directly on the object.

    Syntax

    The syntax for the hasOwnProperty is given below −

     object.hasOwnProperty(propertyName)

    Here,

    • object is an object to which the hasOwnProperty method is applied.
    • PropertyName is the name of the property to look for in the object.

    Example

    Below is an example of using the hasOwnProperty method −

    const animal ={
      species:'Lion',
      habitat:'Grasslands'};
    
    console.log(animal.hasOwnProperty('species')); 
    console.log(animal.hasOwnProperty('habitat')); 
    console.log(animal.hasOwnProperty('diet'));
    Output

    This will produce the following result −

    true
    true
    false
    

    The Prototype Chain

    The prototype chain is used to represent multiple inheritances at various hierarchy levels. We can connect one prototype to another using the steps mentioned below.

    Example

    Here’s an example of the prototype chain in JavaScript −

    let pupil ={
      id:1,};let fee ={
      id:2,};let institution ={
      id:3,};// Level 1 inheritance
    pupil.__proto__ = institution;// Level 2 inheritance
    pupil.__proto__.__proto__ = fee;// Outputs the pupil object's property
    console.log(pupil.id);// Outputs the institution object's property
    console.log(pupil.__proto__.id);

    Output

    Here is the output based on the hierarchy −

    1
    2
    

    Inheriting Methods

    Prototype inheritance inherits both the object’s properties and its methods. We can create a function in the parent class and call it from the child class. We can also include getter and setter methods in the parent class for usage by the child class.

    Example

    Here is the example for demonstrating how you can inherit methods −

    let userBase ={// Parent object
       canRead:true,
       profession:"",showReadPermission:function(){
    
     console.log(this.canRead);},// Setter method to set the profession of usersetinfo(value){this.profession = value;},// Getter method to get profession detailgetinfo(){return${this.profession};},};let writer ={// Child object
    canWrite:true,}; writer.__proto__ = userBase;// Calling parent function writer.showReadPermission();// Calling setter method writer.info ="blogger";// Calling getter method console.log(writer.info);

    Output

    This will produce the following result −

    true
    blogger
    

    Drawbacks of Prototypical Inheritance

    Here is the list of drawbacks you should consider while working with Prototypal inheritance −

    • It limits flexibility because a single __proto__ property can only inherit from one class.
    • Multiple inheritances can only happen at different levels. To inherit a second class we have to use the __proto__.__proto__ property, extending the hierarchy and making tracking more difficult.
    • Prototype relationships can only be created using objects.
    • Accessing elements with the same name as an element in the base class is difficult, that is, properties with the same name may exist in different classes, but they are difficult to access.
    • The same prototypes cannot be inherited since they create a loop.
  • Parse S expressions

    The Lisp programming language family is built around S-expressions. In this article, you will learn about the steps of making a simple S-expression parser. This can form the basis for the Lisp parser.

    Lisp is the easiest language to implement and creating a parser is the first step. We can use a parser generator for this but it is easier to write the parser ourselves. We will use JavaScript.

    What are S-expressions?

    To define nested list data structures, we use s-expressions which are commonly used in Lisp and other functional programming languages. One s-expression can be either one atom or a sequence of s-expressions.

    If you do not know the Lisp language, S-expressions look like this −

    (+ (second (list "xxx" 10)) 20)
    

    This is a data format in which everything is made up of atoms or lists surrounded by parenthesis (atoms from other lists are separated by spaces).

    Like JSON, S-expressions can have a variety of data types. Numbers, strings, and symbols (without quotations) can represent variable names in several languages.

    In addition, you can use a specific dot operator to form a pair like the below.

    (1 . b)
    

    A list can be represented as doted pairs (which means that it is a linked list data structure).

    This is a list −

    (1 2 3 4)
    

    It can be written as −

    (1 . (2 . (3 . (4 . Nil))))
    

    The special symbol “nil” represents the conclusion of an empty list. This format allows you to generate any binary tree. However, we will not use this doted notation in our parser to avoid complicating things.

    What are the Uses of S-expressions?

    S-expressions are used for creating Lisp code, which can also be used to communicate data.

    They are also present in the textual version of WebAssembly. Probably because the parser is easy and you do not have to create your own format. Instead of JSON, use them to communicate between the server and the browser.

    Step-by-step S-expression Parser in JavaScript

    Here are the steps you need to follow for s-expression parser −

    • Tokenize the Input: First, divide the input string into tokens, which can be parenthesis (,) or symbols.
    • Recursive parsing: Tokens are processed recursively to create the structure. When it finds an opening parenthesis, it generates a new list. A closing parenthesis indicates the end of the current list.
    • Base Cases: Symbols (like integers or words) are returned as values but lists are created using expressions within parentheses.

    Example

    The following code converts the input string to readable tokens (symbols, integers, and parentheses). The parse() method loops over each token continuously. When it detects a (, it creates a new list. When it finds a ), it finishes the list. Numbers are parsed as JavaScript numbers; everything else is interpreted as a symbol (string).

    // Function to tokenize the input string into S-expression tokensfunctiontokenize(input){return input
    
      // Add spaces around '('    .replace(/\(/g,' ( ')// Add spaces around ')'.replace(/\)/g,' ) ').trim()// Split by whitespace.split(/\s+/);}// Recursive function to parse tokens into an S-expressionfunctionparse(tokens){if(tokens.length ===0){thrownewError("Unexpected end of input");}// Get the next tokenlet token = tokens.shift();// Start a new listif(token ==='('){let list =[];// Process until we reach a closing parenthesiswhile(tokens[0]!==')'){// Recursively parse the inner expressions  
        list.push(parse(tokens));}
      tokens.shift();// Remove the closing ')'return list;}elseif(token ===')'){thrownewError("Unexpected ')'");}else{// Return an atom (symbol or number) returnatom(token);}}// Function to identify if a token is a number or symbolfunctionatom(token){let number =Number(token);if(!isNaN(number)){// If it's a number, return it return number;}else{// Otherwise, return it as a symbol return token;}}// Usagelet input ="(+ 1 (* 2 3))";// Tokenize the inputlet tokens =tokenize(input);// Parse the tokens into an AST (Abstract Syntax Tree)let ast =parse(tokens);         
    console.log(ast);

    Output

    If you run the above code with the input, the output will be −

    ["+", 1, ["*", 2, 3]]
  • Package Manager

    In JavaScript, a package manager is a tool that makes simple to install, manage, and update libraries, or packages, in your project. You can save time and add functionality without having to start from scratch by using these packages, which are reusable code pieces provided by other developers.

    Need of a Package Manager

    Suppose there were no package managers. The following duties were likely to be completed by hand in that condition −

    • Find all of the packages that are suitable for your project.
    • Verify that the packages do not include any known vulnerabilities.
    • Pick up the packages.
    • Place them where they belong.
    • Keep an eye out for any developments on each of your packages.
    • Update all packages if a new release takes place.
    • Get rid of any packages that are unnecessary.

    It takes a lot of effort and time to manually arrange tens or even hundreds of things. The two most common package managers in JavaScript are as follows −

    Node Package Manager (npm)

    Node Package Manager, or NPM, is a platform that makes it easier for developers to use and share JavaScript code. It is used with Node.js, a JavaScript runtime that makes JavaScript work not only on a browser but also on a server.

    This is what NPM does −

    • NPM makes it simple to add, modify, and remove code parts known as packages. These packages work similarly to the building blocks that programmers use to add specific features to their applications, including managing user input or connecting to a database.
    • Using commands in the terminal, developers may quickly set up or modify code with NPM (for example, npm install to add a package).
    • NPM offers millions of packages that people from all around the world contribute to, so if you need code for a common task, someone has undoubtedly already created a package for it.
    • Many packages are interdependent. NPM keeps an eye on those links and ensures that you have all the resources needed for the code to run.

    Commands

    Here are some of the commands that you can use to take the benefits of NPM −

    • npm install “package-name”: Using this commands you can install a package.
    • npm uninstall “package-name”: By this command you are able to remove a package.
    • npm update “package-name”: Using this command you can update a package.
    • npm init: By this command you can set up a new project and create a package.json file.

    The package.json file

    Every JavaScript project, whether Node.js or a browser application, can be scoped as a npm package that includes its own package information and package.json file is used to describe the project.

    When npm init is used to start a JavaScript/Node.js project, package.json is created with the following basic metadata provided by developers −

    • name: the name of your JavaScript library/project
    • version: It shows the version of your project. This field is often ignored in application development because there looks to be no need for versioning open-source libraries. But it can be used as a source for the deployment’s version.
    • description: The project’s description.
    • license: The project’s license

    Yarn

    Yarn, like NPM, is a platform that allows developers to manage code packages. Facebook created it to address certain problems that developers experienced with NPM, like performance and stability.

    The installation method is divided into three steps −

    • Resolution: Yarn begins resolving dependencies by sending queries to the registry and iteratively looking for each dependency.
    • Fetching: Next, Yarn checks a global cache directory to see if the required package has been downloaded. If it has not already, Yarn gets the package’s tarball and saves it in the global cache so it can work offline and avoid downloading dependencies several times. Tarballs containing dependencies can also be placed in source control for full offline installations.
    • Linking: Finally, Yarn connects everything together by copying all of the required files from the global cache to the local node_modules directory.

    Commands

    Here are some of the commands you can use for using yarn −

    • yarn add “package-name”: You can use this command for installing a package.
    • yarn remove “package-name”: You can use this command for removing a package.
    • yarn upgrade “package-name”: Use this command to update a package.
    • yarn init: You can set up a new project and create a package.json file.

    Yarn uses a lock file (yarn.lock) to verify that all developers on your team are using the same version of packages. This helps to avoid bugs caused by version variations.

    Why Use a Package Manager?

    Both npm and Yarn are widely used, and the decision between the two is frequently based on personal or project preferences. Here are the reasons why you should use package manager −

    • Save time: It saves time by easily installing and utilizing existing libraries.
    • Dependency Management: Automatically manage libraries that your chosen libraries require.
    • Easy Updates: When upgrades or security updates are available, packages can be updated rapidly.
  • Mutability vs Immutability

    If you are a web developer you have seen the terms mutable and immutable objects in JavaScript. But what these words mean, and why do they matter? This section will help you understand.

    • What mutable and immutable objects are
    • The main differences between them
    • How to make and change them
    • The benefits and drawbacks of using each type in your code

    This knowledge is very helpful for writing cleaner, safer, and more reliable JavaScript code.

    Mutable Objects

    Mutable objects in JavaScript are ones that can be changed after they are created, including arrays, objects, functions, and dates. To add, removeor modify values in these objects, use bracket notation (object[“property”]) or dot notation (object.property).

    But, changing these objects can sometimes lead to problems: it can unintentionally affect other parts of your code, make debugging more difficult, and use more memory. Because of this, even if you can directly change mutable objects, it is usually better to avoid doing so to keep your code simpler and more reliable.

    A mutable data type allows you to change it. Mutability lets you change existing values without creating new ones.

    For each object a pointer is added to the stack, pointing to the object in the heap. Consider the following code −

    const student ={
       name:"Aarav",
       age:15,
       hobbies:["drawing","playing guitar","cycling"]};

    On the stack, you will see student, which is a pointer to the actual object on the heap.

    const student2 = student;
    console.log(student);
    console.log(student2);

    When student is assigned to student2, an additional pointer is added to the stack. Now, these pointers are pointing to a single item on the heap.

    Reference data copies pointers rather than values.

    student2.age =25;
    console.log(student);
    console.log(student2);

    Changing the age of student2 affects the age of the student object. You know it is because they both point to the same thing.

    Clone Object Properties

    To clone the properties of an object, use the Object.assign() function with the spread operator. These allow you to update the attributes of the cloned object while keeping the original object’s properties unchanged.

    How Object.assign() Function Works

    The object.assign method replicates properties from one object (the source) to another (the target), returning the updated target object.

    Here is the syntax −

     Object.assign(target, source)

    The method takes two arguments: target and source. The target is the object that receives the new attributes, whereas the source is where the properties originate. The target may be an empty object {}.

    When the source and target have the same key, the source object overwrites the value of the key on the target.

    const student ={
       name:"Aarav",
       age:15,
       hobbies:["drawing","playing guitar","cycling"]}const student2 = Object.assign({}, student);

    The properties of the student object were copied into an empty target.

    student2 now owns its own properties. You can show this by modifying the values of any of its characteristics. Making this adjustment will have no effect on the student object’s property values.

    student2.age =25;
    console.log(student);
    console.log(student2);

    The value of student2.age, which was modified to 25, has no bearing on the value of student.age because they both have separate attributes.

    Immutable Objects

    Immutable objects are those that cannot be modified after it has been created. Strings, numbers, booleans, nulls, and undefined are all immutable in JavaScript. You can not alter their values or characteristics directly.

    Rather, you have to create a new object with the changes that are required. For example, you can combine two strings or add two integers, but this will result in a new string or number that does not change the original ones.

    Immutable objects offer several advantages over mutable ones. They can reduce side effects, make your code easier to understand and maintain, and boost performance.

    Creating Immutable Objects

    There are several methods to build immutable objects in JavaScript. One option is to use the Object.freeze() method which blocks changes to an existing object. For example- if you freeze an array or object and then attempt to edit it you will receive an error or have no effect.

    Another option is to use the const keyword which creates a constant variable that cannot be reassigned. For example- if you declare a string or an integer as const and then try to reassign it, you will receive an error. But const just makes the variable immutable not the object it refers to.

    Let us consider an example −

    const num =46;const newNum = num;

    By looking at the code above, num has been reassigned to newNum. Both num and newNum now have the same value: 46. Changing the value on newNum does not affect the value on num.

    let student1 ="Aarti";let student2 = student1;

    The code above creates a variable called student1 and assigns it to student2.

    student1 ="Saurabh"
    console.log(student1);
    console.log(student2);

    Changing student1 to Saurabh does not affect student2’s initial value. This shows that in primitive data types, actual values are duplicated, so each has its own. Student1 and student2 have distinct stack memory addresses.

    The stack follows the Last-In, First-Out principle. The first thing to enter the stack is the last to leave, and vice versa. Accessing objects saved in the stack is simple.

    Modifying Immutable Objects

    Immutable objects cannot be changed directly, so you have to use other techniques to edit them. One option is to use the spread operator (…), which duplicates the properties or elements of an object or array into a new one. For example, you can use the spread operator to generate a new array with a new item or a new object with a new attribute without changing the originals.

    Another option is to use the Object.assign() method, which transfers the properties of one or more source objects to a target object. For example, you can use Object.assign() to create a new object that inherits the properties of two existing objects without modifying them.

    Using the Spread Operator

    For arrays: Assume you have an array named fruits −

    const fruits =["apple","banana","orange"];

    For creating a new array using an additional item without modifying or altering fruits −

    const newFruits =[...fruits,"grape"];
    console.log(newFruits);// Original array is unchanged
    console.log(fruits);

    Here is the outcome of the above code −

    [ 'apple', 'banana', 'orange', 'grape' ]
    [ 'apple', 'banana', 'orange' ]
    

    For Objects: Let us say you have an object called person −

    const person ={ name:"Maya", age:30};

    For creating a new object with an additional attribute (for example- country) without modifying the person −

    const newPerson ={...person, country:"India"};
    console.log(newPerson);// Original object is unchanged
    console.log(person);

    Following is the output of the above code −

    { name: 'Maya', age: 30, country: 'India' }
    { name: 'Maya', age: 30 }
    

    Using Object.assign()

    For Objects: Object.assign() creates a new object by combining the properties from multiple objects. Assume you have two objects: person and additionalInfo.

    const person ={ name:"Maya", age:30};const additionalInfo ={ country:"India", hobby:"reading"};

    For creating a new object using the properties from both person and additionalInfo −

    const newPerson = Object.assign({}, person, additionalInfo);
    console.log(newPerson);// Original object is unchanged
    console.log(person);

    Here is the output of the following code −

    { name: "Maya", age: 30, country: "India", hobby: "reading" }
    { name: "Maya", age: 30 } 
    

    Benefits of Immutability

    Immutability can have several advantages for your web development projects −

    • One of them is to avoid side effects which are changes in your program’s state or behavior that you did not want or determine.
    • For example- if you send a mutable object as an argument to a function, the function may change the object, affecting other sections of your code that depend on it.
    • But if you supply an immutable object the function cannot change it, allowing you to avoid this problem. Another benefit is improved performance, particularly in frameworks that use a virtual DOM, like React.
    • A virtual DOM is a replica of the real DOM that is updated in memory and then compared to the actual DOM to apply the changes.
    • If you use immutable objects the comparison can be faster and more efficient because you just need to check the reference rather than the value.

    Drawbacks of Immutability

    Immutability can have various disadvantages, which you should be aware of −

    • One of them is to increase memory consumption as you must build new objects every time you wish to change them.
    • This can also have an impact on trash collection which is the process of cleaning up memory by removing unneeded items.
    • For example- if you generate a large number of temporary objects in a loop, you risk overloading memory and slowing trash collection.
    • Another disadvantage is that creating and modifying immutable objects requires the use of additional methods or operators, making your code more verbose and difficult to read.
    • To get the same outcome you may need to write more lines of code or utilize layered spread operators.

    Mutability vs Immutability

    Here is the basic difference between mutability vs immutability in the tabular form −

    AspectMutabilityImmutability
    MeaningYou can change the value after creation.Once created, the value cannot be changed.
    ExampleArrays or objects (e.g., let numbers = [1, 2, 3];’)Strings or numbers (e.g., ‘const name = “Amit”;’)
    Can you change it?Yes, you can change or modify it.No, you cannot change it directly.
    How to change?You modify the original value.You create a new value instead of changing the original.
    Example code (change)‘numbers.push(4);’‘const newName = name + ” Deepak”;’

  • Minifying JS

    JavaScript enables dynamic and interactive features on websites and web apps. It is a key component of modern web development. But as JavaScript files grow larger and more complex, they can significantly affect page load times and overall speed.

    To solve this issue, developers use techniques like code minification to make JavaScript code simpler without compromising functionality.

    What is the “Code Minification”?

    Minimization is another name for minification. Code minification is the process of optimizing code to minimize bandwidth use, save space, and speed up page loads.

    All of the fundamental programming languages, like HTML, CSS, and JavaScript provide code minification. The procedure is not immediate, though. Making code more concise without sacrificing functionality requires some effort.

    JavaScript code must be parsed, compressed and the output obtained in order to minify it. It should be nearly impossible to read with the naked eye once it has been minified. Everything that once made the code readable has been eliminated, including extraneous white spaces, comments, and newline characters.

    It may be necessary to make other code changes, including rewriting local variables, using implicit conditionals, removing block delimiters or inlining methods.

    Need for Minimizing the Code

    Here are the showing the need for minimizing the code in JavaScript −

    • Faster Load Times: Minified code reduces file size which leads to faster download and execution times, ultimately improving the user experience.
    • Bandwidth optimization: Smaller file sizes limit the amount of data exchanged between the server and the client resulting in lower bandwidth and hosting costs.
    • Improved SEO: Search engines like faster-loading pages, which can result in higher search engine rankings and more organic traffic to the website.
    • Enhanced User Experience: Reduced load times boost user engagement and satisfaction while decreasing bounce rates and increasing conversions.

    Un-minified vs Minified JavaScript Code

    Let’s look at some sample code below. The first block contains common, un-minified JavaScript −

    // Program to check if a word is a palindromefunctionisPalindrome(word){// find the length of the wordconst length = word.length;// loop through half of the wordfor(let i =0; i < length /2; i++){// check if first and last characters are the sameif(word[i]!== word[length -1- i]){return'This is not a palindrome';}}return'This is a palindrome';}// take inputconst inputWord ="hello";// call the functionconst result =isPalindrome(inputWord);
    console.log(result);

    Output

    This will generate the below result −

    This is not a palindrome
    

    Now we will see how the same code will look once it has been minified. See the minified version below −

    functionisPalindrome(w){const l=w.length;for(let i=0;i<l/2;i++)if(w[i]!==w[l-1-i])return"This is not a palindrome";return"This is a palindrome"}const inputWord="hello",result=isPalindrome(inputWord);console.log(result);

    As you can see, the second piece of code is significantly smaller and more concise. That means it will load and render faster, reducing page load time and boosting content.

    We have reduced 529 bytes to 324 bytes, gained 205 bytes of free space, and cut page load time by over 40%.

    How to minify JavaScript Code

    You can minify JavaScript code in a variety of ways. Each of these methods takes a different strategy from the others.

    It is nearly impossible to manually minify all of the code in large JavaScript files. Manually minifying JavaScript files is only feasible for small files owing to the time involved.

    To begin the manual JavaScript minification process, open your JavaScript file in your choice text editor and manually delete each space one at a time. It will take a few minutes to clean up all of the gaps and comments in the JavaScript file. Some of these text editors may even support regular expressions, probably speeding up the process significantly.

    The other option is to install minification software on your computer and run it from the command line. You would need to choose the file you want to minify and provide it in the command line switch with the destination file. The remaining tasks would be handled by the minifying tool.

    Code Minification Vs Compression

    While both code minification and compression work to minimize file size, they vary in their approach and purpose −

    • Code Minification: Code minification includes removing unnecessary characters from source code in order to improve readability and load times while maintaining functionality.
    • Compression: Techniques for reducing file size by encoding data more efficiently, which often result in irreversible changes to the file structure. Gzip, Brotli, and deflate are common compression techniques used in network transmission to reduce bandwidth usage.
  • Memoization

    As our systems grow and start doing more complex calculations, the need for speed grows and process optimization becomes necessary. Ignoring this issue results in applications that use a lot of system resources and operate slowly.

    In this chapter, we will discuss memoization, a technique that can significantly reduce processing time when used properly.

    What is Memoization?

    Memorization is a technique for speeding up applications by storing the results of expensive function calls and providing them when the same inputs are used again.

    Let’s try to understand this by dividing the term into smaller parts −

    • Expensive Function Calls: In computer applications, memory and time are the two primary resources. So a function call that uses a lot of these two resources because it is performing a lot of calculations is considered expensive.
    • Cache: A cache is only a short-term data storage system that holds information to allow faster processing of future requests for that information.

    Benefits of Memoization

    After receiving input, a function does the necessary computation, caches the result, and then returns the value. If the same input is received again in the future, the process will not need to be repeated. It would just return the response that was saved in memory. As a result, a code’s execution time will be greatly reduced.

    When to Use Memoization?

    Here are some of the points mentioned while you should use memoization −

    • When a function calls itself. For example, consider the recursive functions.
    • When the function is pure (returns the same value every time it is invoked). If the value changes with each function call, there is no reason for holding it. As a result, we cannot use memoization in JavaScript when the function is impure.
    • When the function has a high temporal complexity. In this case, keeping the results in a cache improves efficiency by reducing time complexity by avoiding re-computation of functions.

    Memoization in JavaScript

    JavaScript memorization is an optimization technique used to minimize the application’s runtime, complexity, and proper use of time and memory. The procedure involves reducing the number of expensive function calls (a function that recursively calls itself and has some overlapping issues) by using an additional space (cache).

    We store the values that were computed in those previous subproblems using memoization. The saved value is then used once again if the identical subproblem comes up, which lowers the time complexity by reducing the need to perform the same calculation repeatedly.

    How Does Memoization Work?

    JavaScript Memoization purely based on two below concepts −

    • Closure
    • High-order function

    Closures

    The Closure is made up of a function enclosed by references to the state. A closure provides access to an outside function’s scope from an inside function. The closure is formed in JavaScript when a function is created.

    let greeting ="Welcome";functionwelcomeMessage(){let user ="Rahul"; 
       console.log(${greeting} to the program, ${user}!);}welcomeMessage();

    Output

    This will generate the below result −

    Welcome to the program, Rahul!
    

    In the above JavaScript code −

    • The variable greeting is a global variable. It can be accessible from anywhere, including the welcomeMessage() function.
    • The variable user is a local variable that can only be used within the welcomeMessage() function.

    Lexical scoping allows for nested scopes, with the inner function having access to the variables specified in the outer scope. Hence, in the code below, the inner function welcome() gets access to the variable user.

    functionwelcomeMessage(){let user ="Rahul";functionwelcome(){
    
      console.log(Greetings, ${user}!);}welcome();}welcomeMessage();</pre>

    Output

    This will procedure the following output −

    Greetings Rahul!
    

    Now we will modify the welcomeMessage() function and rather than invoking the function welcome(), we will return the welcome() function object.

    functionwelcomeMessage(){let user ='Rahul';functionwelcome(){
    
      console.log(Greetings ${user}!);}return welcome;}let greet =welcomeMessage();greet();</pre>

    Output

    If we run this code, we will get the same results as before. But it is important to note that a local variable is often only present during the function's execution.

    This means that after welcomeMessage() is executed, the user variable is no longer available. In this case, when we call gree(), the reference to welcome() remains, as does the user variable. A closure is a function that keeps the outside scope in the inside scope.

    Greetings Rahul!
    

    Higher-Order Functions

    Higher-order functions act on other functions by passing them as arguments or returning them. In the code above, welcomeMessage() is an example of a higher-order function.

    Now, using the well-known Fibonacci sequence, we will look at how memoization uses these concepts.

    Fibonacci sequence: The Fibonacci sequence is a set of numbers that begin and end with one, with the rule that each number (known as a Fibonacci number) is equal to the sum of the two numbers preceding it.

    1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...
    

    So a simple recursive function for this problem will be as follows −

    functionfibonacciSeq(n){if(n <2)return1;returnfibonacciSeq(n -1)+fibonacciSeq(n -2);}

    If we plot the recursion tree for the above function at n=4, it will look like this. As you can see, there are too many unnecessary computations. Let's try to fix this via memoization.

    functionmemoizedFibSeq(num, memo){// Initialize memo array if not provided
       memo = memo ||[1,1];// if num is less than or equal to 1, return the result directlyif(num <=1)return memo[num];// If result is already computed, return it from memoif(memo[num])return memo[num];// Calculate and store the result in memo
       memo[num]=memoizedFibSeq(num -1, memo)+memoizedFibSeq(num -2, memo);return memo[num];}// Calculate the 10th Fibonacci number
    console.log(memoizedFibSeq(10));

    Output

    Here is the outcome of the above code −

    89
    

    We modify the function in the code example above to accept an optional input named memo. We use the cache object as a temporary memory to store Fibonacci numbers and their corresponding indices as keys, which can then be retrieved later in the execution.