JavaScript | Core Concepts

Luisa Novaes - Aug 25 - - Dev Community

1. JavaScript Fundamentals

  • Question: What are variables in JavaScript and what are the differences between var, let, and const?

    • Variables: Containers that store data. You can declare a variable and assign values to it for use in your code.
    • var x let x const
      • var
        • Function scope: If declared inside a function, it can be accessed anywhere within that function.
        • Can be redeclared, which may lead to unexpected behavior.
      • let
        • Block scope: Within a function, it can only be accessed if within the same block { }.
        • Cannot be redeclared (i.e., declaring the same variable again).
        • Can reassign a value to the variable.
      • const
        • Block scope: Within a function, it can only be accessed if within the same block { }.
        • Cannot be redeclared.
        • Cannot reassign a value, except if it is an array or object (properties and elements can be modified).

    Note: In var, let, and const, a variable declared inside a function can only be accessed within that function. However, with var, it does not need to be within the same code block {}.

    function testVar() {
      if (true) {
        var x = 10;  // var is visible throughout the function
      }
      console.log(x);  // Works and prints 10
    }
    
    function testLet() {
      if (true) {
        let y = 20;  // let is only visible within the block {}
      }
      console.log(y);  // Error! y is not available outside the block {}
    }
    
    testVar();  // Works and prints 10
    testLet();  // Causes an error
    
  • Question: How do functions work in JavaScript? What is the difference between a regular function and an arrow function?

    • Functions: Are blocks of code designed to perform tasks. They can be called from different parts of the code to avoid repetition and ensure better organization.
Feature Regular Functions Arrow Functions
Syntax function name(args) { ... } (args) => { ... }
this Context this is dynamic and depends on how the function is called this is lexical, inherited from the scope in which it was defined
arguments Object Available within the function Not available; uses the rest operator (...args) to access arguments
Usage with new Can be used as a constructor (can be called with new) Cannot be used as a constructor (throws an error when using new)
Methods and super Can be used as a method in objects and can access super in classes Cannot access super and should not be used as an object method
Concise Syntax Typically more detailed More concise, ideal for simple functions

2. Closures

  • Question: What is a closure in JavaScript, and what is it used for?
    • An inner function that has access to the variables declared in the outer function.
    • Common uses: Creating private variables, encapsulating code, and maintaining state.
  • Question: Provide an example of how closures can be used to create private variables.
function createCounter() {
    let counter = 0; // private variable

    return function() {
        counter++; // The inner function can access and modify 'counter'
        return counter;
    };
}

const myCounter = createCounter(); // creates a counter

console.log(myCounter()); // Prints 1
Enter fullscreen mode Exit fullscreen mode

3. Execution Context and this

3. Execution Context and this

  • Question: Explain how it works and how the value of this is determined in different scenarios.
    • this refers to the object of the context, and its value depends on how a function is called:
      • In the global context, outside of any function: this refers to the global object (e.g., window in the browser).
      • Inside a method: this refers to the object that owns the method.
      • In DOM events: this refers to the element that triggered the event.
      • With call, apply, or bind: you can explicitly set the value of this.

Examples:

  1. Global Context
console.log(this); // In the browser, this will log the global object `window`
Enter fullscreen mode Exit fullscreen mode
  1. Inside a Method
const person = {
  name: 'John',
  greet: function() {
    console.log(this.name); // `this` refers to the `person` object
  }
};

person.greet(); // Output: John
Enter fullscreen mode Exit fullscreen mode
  1. In DOM Events
<button id="myButton">Click me</button>
<script>
  document.getElementById('myButton').addEventListener('click', function() {
    console.log(this); // `this` refers to the button element that triggered the event
  });
</script>
Enter fullscreen mode Exit fullscreen mode
  1. Using call, apply, or bind
function greet() {
  console.log(`Hello, ${this.name}`);
}

const person = { name: 'Jane' };

greet.call(person); // Output: Hello, Jane

greet.apply(person); // Output: Hello, Jane

const boundGreet = greet.bind(person);
boundGreet(); // Output: Hello, Jane
Enter fullscreen mode Exit fullscreen mode

4. Prototypes and Inheritance

  • Question: What is prototyping in JavaScript? How does prototypical inheritance work?

    • Prototyping is a concept that allows JavaScript objects to inherit properties and methods from other objects. Instead of using classes as in Object-Oriented Programming (OOP), JavaScript uses prototypes.
  • Question: What is the difference between proto and prototype?

Explanation:

  • __proto__: It is an internal property of an object that points to its prototype. You can use it to access the prototype of an object.

  • prototype: It is a property of constructor functions. It is used to define properties and methods that will be shared among all instances created by that constructor.

Examples:

  1. Prototypical Inheritance
// Constructor function
function Animal(name) {
  this.name = name;
}

// Adding a method to the Animal prototype
Animal.prototype.speak = function() {
  console.log(`${this.name} makes a noise.`);
};

const dog = new Animal('Rex');
dog.speak(); // Output: Rex makes a noise.
Enter fullscreen mode Exit fullscreen mode
  1. __proto__ vs prototype
// Constructor function
function Car(make) {
  this.make = make;
}

// Adding a method to the Car prototype
Car.prototype.drive = function() {
  console.log(`${this.make} is driving.`);
};

const myCar = new Car('Toyota');

console.log(myCar.__proto__ === Car.prototype); // Output: true

// `prototype` is used in the constructor function
console.log(Car.prototype); // Output: { drive: [Function (anonymous)] }
Enter fullscreen mode Exit fullscreen mode
  • __proto__ is used to inspect or manipulate the prototype chain, whereas prototype is used to set up properties and methods for objects created by a constructor.

5. Event Loop and Asynchrony

  • Question: Explain how the event loop works in JavaScript.

    • Event Loop: Manages the execution of asynchronous code and ensures that asynchronous operations are handled without blocking the main thread of the code.
  • Question: What is the difference between callbacks, promises, and async/await?

    • Callbacks: Functions passed as arguments, traditionally used to handle asynchronous operations, but can lead to hard-to-maintain code.
    function fetchData(callback) {
      setTimeout(() => {
        callback('Data received');
      }, 1000);
    }
    
    fetchData((data) => {
      console.log(data); // "Data received"
    });
    
    • Promises: Represent the completion of an asynchronous operation and allow chaining operations with .then(), .catch(), and .finally().
    fetchData()
      .then((data) => {
        console.log(data); // "Data received"
      })
      .catch((error) => {
        console.error(error);
      });
    
    function fetchData() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('Data received');
        }, 1000);
      });
    }
    
    • Async/Await: A more modern syntax that simplifies asynchronous code, allowing you to write asynchronous code in a more synchronous and readable manner.

      • await: Pauses the execution of the async function until the Promise is resolved.
      async function showData() {
        try {
          const data = await fetchData();
          console.log(data); // "Data received"
        } catch (error) {
          console.error(error);
        }
      }
      
      showData();
      
      function fetchData() {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve('Data received');
          }, 1000);
        });
      }
      

6. Array and Object Manipulation

  • Question: How can you iterate over arrays and objects in JavaScript? What are the most commonly used methods? Explain the difference between map, filter, and reduce.

    Arrays:

    • for loop
    const numbers = [1, 2, 3, 4, 5];
    for (let i = 0; i < numbers.length; i++) {
      console.log(numbers[i]);
    }
    
    • forEach: Executes a function for each item in the array
    const fruits = ['apple', 'banana', 'orange'];
    fruits.forEach((fruit) => {
      console.log(fruit);
    });
    
    • map: Creates a new array with the results of applying a function to each item in the array. Transforms data.
    const numbers = [1, 2, 3];
    const squares = numbers.map(num => num * num);
    console.log(squares); // [1, 4, 9]
    
    • filter: Creates a new array with the elements that pass a test. Filters data.
    const numbers = [1, 2, 3, 4, 5];
    const evenNumbers = numbers.filter(num => num % 2 === 0);
    console.log(evenNumbers); // [2, 4]
    
    • reduce: Applies a reducing function to each item in the array, resulting in a single value. Accumulates values, sums.
    const numbers = [1, 2, 3, 4];
    const sum = numbers.reduce((accumulator, number) => accumulator + number, 0);
    console.log(sum); // 10
    
  • Objects:

    • for in loop: Iterates over the properties of an object.
    const person = { name: 'John', age: 30, city: 'São Paulo' };
    for (let key in person) {
      console.log(`${key}: ${person[key]}`);
    }
    
    • Object.keys: Returns an array with the names of the properties of an object.
    const person = { name: 'Ana', age: 25 };
    Object.keys(person).forEach(key => {
      console.log(`${key}: ${person[key]}`);
    });
    
    • Object.values: Returns an array with the values of the properties of an object.
    const person = { name: 'Lucas', age: 28 };
    Object.values(person).forEach(value => {
      console.log(value);
    });
    
    • Object.entries: Returns an array with the [key, value] pairs of an object.
    const person = { name: 'Beatriz', age: 22 };
    Object.entries(person).forEach(([key, value]) => {
      console.log(`${key}: ${value}`);
    });
    

7. Scope and Hoisting

  • Question: What is scope in JavaScript? What is the difference between global scope, function scope, and block scope?

    • Scope is the context in which variables and functions are declared and accessed.
      • Global: Accessible throughout the entire code.
      • Function: Accessible only within the function. Good for encapsulating and keeping things clean.
      • Block: Accessible only within a block { } (e.g., declared within an if statement, or within a function). Adds an additional level of encapsulation.
  • Question: What is hoisting in JavaScript?

    • Hoisting is a feature of JavaScript's compilation phase. During this phase, JavaScript prepares the code for execution. This involves moving variable and function declarations to the top of their scope to ensure they are available for use throughout the code.
      • Functions: The declaration is fully hoisted, making the function available throughout the scope.
      • Variables (var): Only the declaration is hoisted. The variable is undefined until the initialization line.
      • Variables (let and const): The declaration is hoisted, but cannot be accessed before the declaration line due to the "temporal dead zone."
    • The goal is to ensure functions are available anywhere in the scope, allowing greater flexibility in the order of function declarations and calls.

8. DOM Manipulation

  • Question: How can you manipulate the DOM in JavaScript? What are the most common methods used for this?
    • DOM manipulation is the process of manipulating the content of an HTML document using JavaScript.
    • Common Methods:
      • Select Elements:
        • getElementById(id)
        • getElementsByClassName(className)
        • getElementsByTagName(tagName)
        • querySelector(selector)
        • querySelectorAll(selector)
      • Change Content:
        • innerHTML: Sets or returns the HTML content.
        • textContent: Sets or returns the text content.
      • Change Attributes:
        • setAttribute(name, value)
        • getAttribute(name)
      • Add or Remove Classes:
        • classList.add(className)
        • classList.remove(className)
        • classList.toggle(className): Adds if not present, and removes if present.
      • Create and Insert Elements:
        • createElement(tagName)
        • appendChild(child)
        • insertBefore(newNode, referenceNode)
      • Remove Elements:
        • removeChild(child)
        • remove()
  • Question: What is the difference between innerHTML and textContent?
    • innerHTML manipulates the HTML content. textContent manipulates the text content.

9. State Management and Data Flow

  • Question: How do you manage the state of an application in JavaScript, especially in frameworks like React?
    • State management reflects how data is stored and manipulated in an application. In React, there are various methods:
      • Local: Using the useState hook. Shares data within a functional component.
      • Global: Using Context API or Redux. Shares data between components.
  • Question: What is the unidirectional data flow pattern and how does it apply to frontend development?
    • Unidirectional Data Flow is a pattern where data flows in one direction through the application. This means that information flows top-down, and changes in state are propagated back to components that need to be updated.
      • Parent and Child Components:
        • Data is passed from the parent component to child components via props.
        • Child components should not modify data directly; instead, they can request an update from the parent component.
      • State Updates:
        • When an event occurs (like a click), the state is updated. State updates cause React to re-render the affected components, reflecting the changes in the user interface.
    • The unidirectional data flow pattern is a fundamental approach in modern frontend development, especially in frameworks and libraries like React. It provides a predictable and organized way to manage state and update the user interface.

10. Testing in JavaScript

  • Question: What are some best practices for writing tests in JavaScript?
    • Keep your tests simple and isolated: Each test should check a single behavior or unit of code. This makes it easier to identify problems and maintain the tests.
    • Name your tests clearly: Use descriptive names so it's easy to understand what the test is checking. This helps quickly identify what is breaking when a test fails.
    • Write tests before code (TDD): Test-Driven Development (TDD) is a practice where you write the tests before implementing the code. This can help define what the code should do clearly and ensure you cover all use cases.
    • Use meaningful asserts: Make sure that the assertions in your tests check for expected behavior and not just the presence of data.
    • Keep tests fast and independent: Tests should be quick so they can be run frequently. Also, they should be independent so that the execution of one test does not affect others.
    • Maintain good isolation: Use mocks and stubs to isolate the code under test and avoid external dependencies, such as network calls or interactions with databases.
    • Organize your tests: Keep your tests well-organized in folders and files that reflect the structure of your code. This makes it easier to locate and run tests.
    • Adequate test coverage: While test coverage should not be the only quality metric, have adequate coverage to ensure that important parts of your code are being tested.
    • Review and update tests regularly: Update your tests when you make changes to the code. Outdated tests can give a false sense of security.
    • Document complex tests: When tests are complex or have special logic, document the reason and what is being checked. This helps understand the purpose of the test in the future.
  • Question: Can you explain the difference between unit tests, integration tests, and end-to-end (E2E) tests?

    • Unit Tests

      • Objective: Verify the functionality of individual units of code, such as functions, methods, or classes.
      • Scope: Test isolated components and check if each unit performs its tasks correctly.
      • Example: Test a function that calculates the total value of a shopping cart based on items and discounts applied.
      • Common Tools: Jest, Mocha, Jasmine.
      • Code Example:

        // Function to be tested
        function sum(a, b) {
            return a + b;
        }
        
        // Unit test using Jest
        test('sum of 1 and 2 should be 3', () => {
            expect(sum(1, 2)).toBe(3);
        });
        
    • Integration Tests

      • Objective: Verify the interaction between different units of code or components.
      • Scope: Test the integration of various parts of the system to ensure they work correctly together.
      • Example: Test if a function that makes API calls and manipulates data interacts correctly with another function that updates the user interface.
      • Common Tools: Jest (can also be used for integration), Mocha, Cypress.
      • Code Example:

        // Function that uses a fictitious API
        async function fetchData() {
            const response = await fetch('https://api.example.com/data');
            return response.json();
        }
        
        // Integration test using Jest and mocks
        test('fetchData should return data from the API', async () => {
            global.fetch = jest.fn(() =>
                Promise.resolve({
                    json: () => Promise.resolve({ key: 'value' }),
                })
            );
        
            const data = await fetchData();
            expect(data.key).toBe('value');
        });
        
    • End-to-End (E2E) Tests

      • Objective: Test the complete flow of the system, from the user interface to the backend and interactions with databases or external services.
      • Scope: Test the system as a whole to ensure all components work together as expected.
      • Example: Test the entire process of a purchase on an e-commerce site, from selecting an item to completing the purchase and receiving a confirmation.
      • Common Tools: Cypress, Selenium, TestCafe.
      • Code Example (Cypress):

        // End-to-end test using Cypress
        describe('Purchase Flow', () => {
            it('should complete a purchase successfully', () => {
                cy.visit('https://www.example.com');
                cy.get('.product').first().click();
                cy.get('.add-to-cart').click();
                cy.get('.checkout').click();
                cy.get('.order-confirmation').should('contain', 'Thank you for your purchase!');
            });
        });
        

11. Types and Type Coercion

  • Question: What are the primitive types in JavaScript?

    • Number: Represents numbers, both integers and floating-point. Example: 42, 3.14.
    • String: Represents sequences of characters. Example: "hello", 'world'.
    • Boolean: Represents logical values, which can be true or false.
    • Undefined: Represents the absence of value. A variable that has been declared but not initialized has the value undefined.
    • Null: Represents intentional absence of value. It is a value that should be explicitly assigned to a variable to indicate that it has no value.
    • Symbol: Represents unique and immutable identifiers. Used to create object properties that are guaranteed to be unique.
    • BigInt: Represents very large integers that cannot be represented by the Number class. Example: 1234567890123456789012345678901234567890n.
    • Code Example:

      // Primitive types
      let num = 42; // Number
      let str = "Hello"; // String
      let bool = true; // Boolean
      let undef; // Undefined
      let nul = null; // Null
      let sym = Symbol('unique'); // Symbol
      let bigInt = 1234567890123456789012345678901234567890n; // BigInt
      
  • Question: How does type coercion work in JavaScript?

    • Type coercion in JavaScript refers to the automatic or explicit conversion between different data types. It can be implicit (automatic) or explicit (forced by the code).
    • Implicit

      • Arithmetic Operations: When using the + operator with a string and a number, the number is converted to a string, and the operation is performed as concatenation.

        let result = "The number is " + 5; // "The number is 5"
        
      • Comparative Operations: When comparing a number with a string, the string is converted to a number.

        console.log(5 == '5'); // true, because '5' is converted to 5
        
      • Boolean Context: When a value is used in a context that expects a boolean, such as an if statement, JavaScript converts the value to true or false.

        if ("hello") {
            console.log("This will run"); // The non-empty string is converted to true
        }
        
    • Explicit Coercion

      • You can force type coercion manually using conversion functions. Type coercion can help avoid bugs and write more predictable and robust code.
      • String: To convert a value to a string, you can use String() or the toString() method.

        let num = 123;
        let str = String(num); // "123"
        
      • Number: To convert a value to a number, you can use Number(), parseInt(), or parseFloat().

        let str = "456";
        let num = Number(str); // 456
        
      • Boolean: To convert a value to a boolean, you can use Boolean().

        let num = 0;
        let bool = Boolean(num); // false
        

12. ES6+ and Modern Features

  • Question: What are the new features introduced in ES6 and later versions?

ES6 (2015) introduced several significant improvements to JavaScript. Here are some of the key features:

  1. Let and Const: Variable declarations with block scope.
  2. Arrow Functions: Concise syntax for functions, with lexically bound this.

    const add = (a, b) => a + b;
    
  3. Classes: Prototype-based syntax for creating objects and inheritance.

    class Person {
      constructor(name) {
        this.name = name;
      }
      greet() {
        return `Hello, ${this.name}`;
      }
    }
    
  4. Template Literals: Strings with variable interpolation and multi-line support.

    const name = 'Alice';
    const message = `Hello, ${name}!`;
    
  5. Destructuring Assignment: Extracting values from arrays or objects into distinct variables.

    const [a, b] = [1, 2];
    const { x, y } = { x: 10, y: 20 };
    
  6. Default Parameters: Default values for function parameters.

    function greet(name = 'Guest') {
      return `Hello, ${name}`;
    }
    
  7. Rest Parameters: Collecting multiple parameters into an array.

    function sum(...numbers) {
      return numbers.reduce((total, num) => total + num, 0);
    }
    
  8. Spread Operator: Expanding elements of arrays or properties of objects.

    const arr1 = [1, 2];
    const arr2 = [...arr1, 3, 4];
    
  9. Promises: For handling asynchronous operations.

    const fetchData = () => new Promise((resolve, reject) => {
      setTimeout(() => resolve('Data fetched'), 1000);
    });
    
  10. Modules: Importing and exporting modules.

    // module.js
    export const pi = 3.14;
    
    // app.js
    import { pi } from './module.js';
    
  11. Enhanced Object Literals: Improved syntax for objects.

    const name = 'Alice';
    const person = {
      name,
      greet() {
        return `Hello, ${this.name}`;
      }
    };
    

ES7 (ECMAScript 2016) and ES8 (ECMAScript 2017) introduced additional features:

  1. ES7 (2016)

    • Exponentiation Operator: ** for exponentiation.

      const result = 2 ** 3; // 8
      
  2. ES8 (2017)

    • Async/Await: Syntax for writing asynchronous code in a more synchronous manner.

      async function fetchData() {
        const response = await fetch('https://api.example.com');
        const data = await response.json();
        return data;
      }
      
- **Object.entries() and Object.values()**: Methods for iterating over the keys and values of objects.
Enter fullscreen mode Exit fullscreen mode
    ```javascript
    const obj = { a: 1, b: 2 };
    console.log(Object.entries(obj)); // [['a', 1], ['b', 2]]
    console.log(Object.values(obj)); // [1, 2]
    ```
Enter fullscreen mode Exit fullscreen mode
  1. ES9 (ECMAScript 2018) and ES10 (ECMAScript 2019) brought further improvements:

    • Rest/Spread Properties: In objects, similar to usage with arrays.
    const obj = { a: 1, b: 2 };
    const clone = { ...obj }; // { a: 1, b: 2 }
    
  • Asynchronous Iteration: Using for-await-of to iterate over asynchronous objects.

    async function* asyncGen() {
      yield 1;
      yield 2;
    }
    for await (let value of asyncGen()) {
      console.log(value);
    }
    
  • Optional Catch Binding: The catch block parameter can be omitted.

    try {
      // Code
    } catch {
      // Handle error without using the error object
    }
    
  • flat() and flatMap(): Methods for flattening arrays and mapping arrays.

    const arr = [1, [2, [3, 4]]];
    console.log(arr.flat(2)); // [1, 2, 3, 4]
    
  • Question: Explain how destructuring and spread/rest operators work.

    Destructuring

    Destructuring allows you to extract values from arrays or objects and assign them to distinct variables. It can be used with both arrays and objects.

    • Destructuring with Arrays:
    const numbers = [1, 2, 3];
    const [first, second] = numbers; // first = 1, second = 2
    
    • Destructuring with Objects:
    const person = { name: 'Alice', age: 25 };
    const { name, age } = person; // name = 'Alice', age = 25
    

    You can also rename variables and set default values:

    const { name: fullName = 'Unknown', age = 30 } = person;
    

    Spread/Rest Operators

    Spread Operator:

    The spread operator (...) is used to expand elements of arrays or properties of objects into new arrays or objects.

    • In Arrays:
    const arr1 = [1, 2];
    const arr2 = [3, 4];
    const combined = [...arr1, ...arr2]; // [1, 2, 3, 4]
    
    • In Objects:
    const obj1 = { a: 1, b: 2 };
    const obj2 = { c: 3 };
    const merged = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3 }
    

    Rest Parameters:

    The rest parameter (...) allows you to accept an indefinite number of arguments as an array.

    • In Functions:
    function sum(...numbers) {
      return numbers.reduce((total, num) => total + num, 0);
    }
    

    Here, numbers is an array containing all arguments passed to the sum function.

13. JavaScript Security

  • Question: What are some of the main security concerns when writing JavaScript for web applications?
  • Question: What are XSS and CSRF, and how can you mitigate these types of attacks?

Main Security Concerns in JavaScript for Web Applications

  1. Cross-Site Scripting (XSS):

    • Description: XSS occurs when an attacker injects malicious scripts into web pages that are executed in the user’s browser. This can lead to cookie theft, session data compromise, or unwanted actions performed on behalf of the user.
    • Prevention:
      • Output Escaping: Always escape and sanitize dynamic data inserted into HTML to prevent scripts from executing.
      • Use Content Security Policy (CSP): Set up a CSP to restrict the execution of unauthorized scripts.
      • Data Validation and Sanitization: Validate and sanitize data both on the client side and the server side.
  2. Cross-Site Request Forgery (CSRF):

    • Description: CSRF occurs when an attacker tricks an authenticated user into performing unauthorized actions on a site where they are logged in. For example, a user might be tricked into submitting a request that changes account settings or performs a financial transaction unknowingly.
    • Prevention:
      • CSRF Tokens: Generate and verify unique CSRF tokens for each request. Include the token in forms and request headers.
      • Referer and Origin Checking: Check the Referer or Origin headers of requests to ensure they originate from your own site.
      • Secure Request Methods: Use secure HTTP methods like POST for critical operations and avoid allowing sensitive actions to be performed using GET methods.
  3. Code Injection:

    • Description: Code injection occurs when an attacker inserts malicious code into an application, often through input fields. This can lead to attacks such as SQL injection if not properly handled.
    • Prevention:
      • Prepared Statements: Use parameterized queries and ORM for database interactions.
      • Input Sanitization and Validation: Validate and sanitize all input data to prevent malicious code from being processed.
  4. Session Security:

    • Description: Ensuring user sessions are secure is crucial to prevent attacks such as session hijacking.
    • Prevention:
      • Secure Cookies: Use cookies with the HttpOnly, Secure, and SameSite flags to protect session cookies.
      • Session Management: Implement session token expiration and rotation.
  5. Data Security:

    • Description: Protecting sensitive data is essential to ensure user data privacy and integrity.
    • Prevention:
      • Encryption: Encrypt sensitive data in transit (using HTTPS) and at rest (in storage).
      • Password Management: Use secure hashing algorithms, such as bcrypt, for storing passwords.
. . . . . .
Terabox Video Player