JavaScript

JavaScript is a widely-used programming language for web development, known for its ability to create dynamic and interactive content on websites. It runs on web browsers and is essential for client-side scripting, enabling tasks like form validation, animations, and asynchronous communication with servers.

Variables


  • Variables are containers for storing data values. 
  • In JavaScript, you can declare variables using the var, let, or const keywords. 
  • var is function-scoped.
  • let is block-scoped.
  • const is also block-scoped but its value cannot be reassigned once declared.

var name = "John";
let age = 30;
const PI = 3.14; 

Data Types


JavaScript has primitive data types:
  • String: Represents textual data. Enclosed within single or double quotes.
  • Number: Represents numeric data, both integers and floating-point numbers.
  • Boolean: Represents logical values, true or false.
  • Null: Represents the intentional absence of any object value.
  • Undefined: Represents an uninitialized variable.
Additionally, JavaScript has a non-primitive data type:
  • Object: Represents a collection of key-value pairs, where keys are strings and values can be any data type.

let name = "Alice"; // String
let age = 25; // Number
let isStudent = true; // Boolean
let person = null; // Null
let job; // Undefined
let user = { name: "Bob", age: 30 }; // Object 

Operators


Arithmetic Operators:
Used for basic mathematical operations.

let sum = 5 + 3;
let difference = 10 - 4;
let product = 2 * 6;
let quotient = 8 / 2;
let remainder = 10 % 3; 

Assignment Operators:
Used to assign values to variables.

let x = 10;
x += 5; // Equivalent to: x = x + 5; 

Comparison Operators:
Used to compare values.

let a = 5;
let b = 10;
console.log(a === b); // false
console.log(a < b); // true 

Logical Operators:
Used to combine conditional statements.

let isAdult = true;
let hasLicense = false;
console.log(isAdult && hasLicense); // false
console.log(isAdult || hasLicense); // true 

If Statement


if statement:
It executes a block of code if a specified condition is true.

let x = 10;
if (x > 5) {
     console.log("x is greater than 5");

if...else statement:
It executes one block of code if the condition is true, and another block if the condition is false.

let age = 20;
if (age >= 18) {
     console.log("You are an adult");
} else {
     console.log("You are a minor");

else if statement:
It allows you to specify multiple conditions to execute different blocks of code.

let time = 14;
if (time < 12) {
     console.log("Good morning");
} else if (time < 18) {
     console.log("Good afternoon");
} else {
     console.log("Good evening");

Loops


for loop:
It repeats a block of code a specified number of times.

for (let i = 0; i < 5; i++) {
     console.log("Iteration " + i);

while loop:
It repeats a block of code while a specified condition is true.

let i = 0;
while (i < 5) {
     console.log("Iteration " + i);
     i++;

do...while loop:
It is similar to the while loop but ensures that the block of code is executed at least once, even if the condition is false.

let j = 0;
do {
     console.log("Iteration " + j);
     j++;
 } while (j < 5); 

for...in loop:
It iterates over the properties of an object

const person = { name: "Alice", age: 30 };
for (let key in person) {
     console.log(key + ": " + person[key]);

for...of loop:
It iterates over iterable objects like arrays, strings, etc.

const colors = ["red", "green", "blue"];
for (let color of colors) {
     console.log(color);

Functions


  • A function is a block of reusable code that performs a specific task.
  • Functions can take parameters (inputs) and return values (outputs).

Defining a function:

function greet(name) {
     return "Hello, " + name + "!";

Calling a function:

let greeting = greet("Alice");
console.log(greeting); // Output: Hello, Alice! 

Function Scope


  • Scope determines the visibility and accessibility of variables within your code.
  • JavaScript has function scope and block scope.

Variables declared inside a function 
are scoped to that function and are not accessible outside of it.

function myFunction() {
     let x = 10;
     console.log(x); // Output: 10
}

myFunction();
console.log(x); // Error: x is not defined 

Nested functions
have access to variables declared in their outer scope, a concept known as lexical scoping.

function outerFunction() {
     let outerVariable = "I'm from outer function";

          function innerFunction() {
         console.log(outerVariable); // Accessing outerVariable from outer scope
     }

          innerFunction();
}
outerFunction(); // Output: I'm from outer function 

Block Scope
(introduced in ES6 with let and const)


Variables declared with let and const are block-scoped
meaning they are only accessible within the block in which they are defined.

if (true) {
     let y = 20;
     const z = 30;
     console.log(y); // Output: 20
     console.log(z); // Output: 30
}

console.log(y); // Error: y is not defined
console.log(z); // Error: z is not defined 

Hoisting


  • In JavaScript, variable declarations and function declarations are hoisted to the top of their containing scope.
  • Understanding functions and scope is crucial for writing maintainable and bug-free JavaScript code.

However, only the declarations are hoisted, not the initializations.

console.log(a); // Output: undefined
var a = 5; 

is equivalent to:

var a;
console.log(a); // Output: undefined
a = 5; 

Arrays


  • An array is an ordered collection of values, where each value is identified by an index.
  • Arrays can hold values of any data type, including other arrays (nested arrays).

Defining an Array:

let colors = ["red", "green", "blue"]; 

Accessing Elements:

console.log(colors[0]); // Output: red

Modifying Elements:

colors[1] = "yellow";
console.log(colors); // Output: ["red", "yellow", "blue"] 

Array Methods:

  • push(): Adds one or more elements to the end of an array. 
  • pop(): Removes the last element from an array. 
  • shift(): Removes the first element from an array. 
  • unshift(): Adds one or more elements to the beginning of an array. 
  • splice(): Adds or removes elements from an array at a specified index. 
  • concat(): Combines two or more arrays. 
  • slice(): Extracts a section of an array and returns a new array. 
  • forEach(): Executes a provided function once for each array element. 
  • map(): Creates a new array with the results of calling a provided function on every element in the calling array. 
  • filter(): Creates a new array with all elements that pass the test implemented by the provided function. 
  • reduce(): Executes a reducer function on each element of the array, resulting in a single output value. 

Objects


  • An object is a collection of key-value pairs where each key is a string (or Symbol) and each value can be of any data type.

Defining an Object:

let person = {
     name: "Alice",
     age: 30,
     isStudent: false
 }; 

Accessing Properties:

console.log(person.name); // Output: Alice 

Modifying Properties:

person.age = 35;
console.log(person); // Output: { name: "Alice", age: 35, isStudent: false } 

Adding New Properties:

person.city = "New York";
console.log(person); // Output: { name: "Alice", age: 35, isStudent: false, city: "New York" } 

Object Methods:

  • Object.keys(): Returns an array containing the names of all enumerable properties of an object. 
  • Object.values(): Returns an array containing the values of all enumerable properties of an object. 
  • Object.entries(): Returns an array containing arrays of key-value pairs for each enumerable property of an object. 
  • Object.assign(): Copies the values of all enumerable own properties from one or more source objects to a target object.

JavaScript Fundamentals: ES6 Features
Arrow functions, template literals, destructuring, etc.


  • These ES6 features significantly enhance JavaScript's capabilities and productivity, enabling developers to write more concise, readable, and maintainable code. 
  • They are widely used in modern JavaScript development, including Cypress test automation.

Arrow Functions:

  • Arrow functions provide a more concise syntax for defining functions. 
  • They lexically bind the this value, making it easier to work with callbacks and maintain context.

// Traditional function
function add(a, b) {
     return a + b;
}

// Arrow function
const add = (a, b) => a + b; 

Template Literals:

  • Template literals allow embedding expressions and multiline strings directly within backticks (`). 
  • They provide a cleaner and more readable way to concatenate strings.

const name = "Alice";
console.log(`Hello, ${name}!`); 

Destructuring:

  • Destructuring provides a concise syntax for extracting values from arrays and objects and assigning them to variables. 
  • It simplifies working with complex data structures.

const person = { name: "Bob", age: 30 };
const { name, age } = person;
console.log(name, age); // Output: Bob 30 

Spread Syntax:

  • The spread syntax allows an iterable (like an array or string) to be expanded in places where zero or more arguments or elements are expected. 
  • It is useful for copying arrays, concatenating arrays, and passing function arguments.

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combinedArray = [...arr1, ...arr2];
console.log(combinedArray); // Output: [1, 2, 3, 4, 5, 6] 

Rest Parameters:

  • Rest parameters allow functions to accept an indefinite number of arguments as an array. 
  • They simplify working with functions that take a variable number of arguments.

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

console.log(sum(1, 2, 3, 4, 5)); // Output: 15 

Object Literal Enhancements:

  • ES6 introduced shorthand syntax for defining object properties and methods. 
  • It reduces redundancy and makes object definitions more concise. 

const name = "Alice";
const age = 30;
const person = { name, age, greet() { console.log(`Hello, ${this.name}!`); } };
 person.greet(); // Output: Hello, Alice! 

Promises:

  • Promises provide a cleaner way to handle asynchronous operations and avoid callback hell. 
  • They represent a value that may be available now, or in the future, or never.

const fetchData = () => {
     return new Promise((resolve, reject) => {
         // Simulating asynchronous operation
         setTimeout(() => {
             const dataIsAvailable = true;
             if (dataIsAvailable) {
                 resolve("Data fetched successfully");
             } else {
                 reject("Error fetching data");
             }
         }, 2000);
     });
 };

 fetchData()
     .then(data => console.log(data))
     .catch(error => console.error(error)); 

DOM Manipulation 
Understanding the Document Object Model (DOM)


  • The Document Object Model (DOM) is a programming interface for web documents. 
  • It represents the structure of HTML and XML documents as a tree-like structure, where each node represents a part of the document, such as elements, attributes, and text.
  • Understanding the DOM and how to manipulate it is essential for building dynamic and interactive web applications, as well as for writing automated tests with Cypress, which often involves interacting with elements on web pages.

Tree Structure:

  • The DOM represents an HTML document as a hierarchical tree structure. 
  • The document node represents the entire document. 
  • Elements, attributes, and text nodes are nested within each other to represent the document's structure.

Nodes:

  • Each element, attribute, and text content in an HTML document is represented by a node in the DOM tree. 
  • There are different types of nodes, such as element nodes, text nodes, attribute nodes, comment nodes, etc.

Element Nodes:

  • Element nodes represent HTML elements, such as < div >, < p >, < a > , etc. 
  • They can have child nodes (other elements, text nodes, etc.) and attributes.

Accessing DOM Elements:

  • JavaScript allows you to interact with the DOM dynamically, enabling you to access, manipulate, and modify elements on a web page. 
  • Common methods for accessing elements include document.getElementById(), document.querySelector(), and document.querySelectorAll().

< !DOCTYPE html >
< html lang="en" >
< head >
         < meta charset="UTF-8" >
         < meta name="viewport" content="width=device-width, initial-scale=1.0" >
         < title > DOM Manipulation < /title >
< /head >
< body >
         < div id="myDiv" class="container" >
                  < p > Hello, world! < /p >
         < /div >
         < script >
                  // Get element by ID
                  const divElement = document.getElementById("myDiv");

                  // Get element by CSS selector
                  const pElement = document.querySelector("p");
          < /script >
< /body >
< /html > 

Selecting DOM Elements:

//getElementById: Selects an element by its ID attribute.
const elementById = document.getElementById("myElementId");

//getElementsByClassName: Selects elements by their class name.
const elementsByClassName = document.getElementsByClassName("myClassName");

//getElementsByTagName: Selects elements by their tag name.
const elementsByTagName = document.getElementsByTagName("div");

//querySelector: Selects the first element that matches a specified CSS selector.
const elementByQuerySelector = document.querySelector("#myElementId");

//querySelectorAll: Selects all elements that match a specified CSS selector.
const elementsByQuerySelectorAll = document.querySelectorAll(".myClassName");

Manipulating DOM Elements:

  • Once you have access to an element, you can manipulate its properties, attributes, and content using JavaScript. 
  • Common operations include changing text content, modifying styles, adding or removing classes, and handling events.

//textContent: Modifies the text content of an element.
element.textContent = "New text content";

//innerHTML: Modifies the HTML content of an element.
element.innerHTML = "< strong > New HTML content < /strong >"

//setAttribute: Sets the value of an attribute on an element.
element.setAttribute("href", "https://example.com");

//style: Modifies CSS properties of an element.
element.style.backgroundColor = "red";

// Add class
divElement.classList.add("highlight");  

// Remove class
divElement.classList.remove("container");  

// Add event listener
divElement.addEventListener("click", () => {
     console.log("Div clicked");
 }); 

Creating and Appending DOM Elements:

// createElement: Creates a new element.
const newElement = document.createElement("div");

// Set text content
newParagraph.textContent = "This is a new paragraph";  

// appendChild: Appends a child element to a parent element. 
parentElement.appendChild(newElement);

Removing DOM Elements:

// removeChild: Removes a child element from its parent.
parentElement.removeChild(childElement);

Querying Multiple Elements:

// Get all paragraphs in the document
const allParagraphs = document.querySelectorAll("p");  

// Loop through and manipulate each paragraph
allParagraphs.forEach(paragraph => {
     paragraph.style.color = "red";
 }); 

Event Handling


  • Event handling in the DOM allows JavaScript code to respond to user interactions with a web page, such as clicks, mouse movements, keyboard actions, and more.
  • Event handling is a fundamental aspect of web development and is extensively used in building interactive web applications and writing test scripts with Cypress for simulating user actions.

Adding Event Listeners:

  • Use the addEventListener method to attach an event handler function to an element. 
  • This method takes two arguments: the event type and the event handler function.

const button = document.getElementById("myButton");
button.addEventListener("click", () => {
     console.log("Button clicked");
}); 

Event Object:

  • When an event occurs, an event object is created and passed as an argument to the event handler function. 
  • This object contains information about the event, such as the type of event, the target element, mouse coordinates, and more.

button.addEventListener("click", (event) => {
     console.log("Button clicked");
     console.log("Target element:", event.target);
}); 

Event Types:

  • There are many types of events in the DOM, such as click, mouseover, mouseout, keydown, keyup, and more.
  • You can listen for any of these events and respond accordingly.

const input = document.getElementById("myInput");
input.addEventListener("keydown", (event) => {
     console.log("Key pressed:", event.key);
}); 

Removing Event Listeners:

  • To remove an event listener, use the removeEventListener method.
  • Pass the same event type and event handler function that you used to add the listener.

const button = document.getElementById("myButton");
const handleClick = () => {
     console.log("Button clicked");
};
button.addEventListener("click", handleClick);
// Later, remove the event listener
button.removeEventListener("click", handleClick); 

Event Bubbling and Event Capturing:

  • By default, when an event occurs on an element, it bubbles up through its ancestor elements, triggering any event listeners attached to those elements. This is called event bubbling. 
  • Event capturing is the opposite; the event is captured from the top of the DOM hierarchy down to the target element. 
  • You can control whether to use event capturing or event bubbling by passing a third argument to addEventListener, true for event capturing and false (or omitting) for event bubbling.

Asynchronous JavaScript


  • Asynchronous JavaScript is crucial for handling operations that may take time to complete, such as fetching data from a server, reading a file, or waiting for user input.
  • here are several techniques for managing asynchronous operations in JavaScript.
  • These techniques help in writing asynchronous JavaScript code that is more readable, maintainable, and error-resistant. 
  • They are extensively used in web development, including frontend frameworks like React, as well as in test automation with Cypress for handling asynchronous tasks like waiting for elements to appear or disappear.

Callbacks:

  • Callbacks are functions that are passed as arguments to other functions and are executed once the asynchronous operation is complete. 
  • They are a traditional way of handling asynchronous code in JavaScript.

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

  fetchData((data) => {
     console.log(data);
 }); 

Promises:

  • Promises are objects representing the eventual completion or failure of an asynchronous operation. 
  • They provide a cleaner and more structured way to handle asynchronous code compared to callbacks. 
  • Promises have three states: pending, fulfilled, or rejected.

function fetchData() {
     return new Promise((resolve, reject) => {
         setTimeout(() => {
             resolve("Data fetched successfully");
         }, 2000);
     });
 }

  fetchData()
     .then((data) => {
         console.log(data);
     })
     .catch((error) => {
         console.error(error);
     }); 

Async/await:

  • Async/await is a modern syntax for working with asynchronous code in JavaScript. 
  • It allows you to write asynchronous code in a synchronous-looking manner, making it easier to understand and maintain. 
  • async functions return a Promise, and await is used to pause the execution of the function until a Promise is settled.

async function fetchData() {
     return new Promise((resolve, reject) => {
         setTimeout(() => {
             resolve("Data fetched successfully");
         }, 2000);
     });
 }

async function fetchDataAndLog() {
     try {
         const data = await fetchData();
         console.log(data);
     } catch (error) {
         console.error(error);
     }
 }

fetchDataAndLog();