Skip to content

Latest commit

 

History

History
2684 lines (1845 loc) · 109 KB

JavaScript-Quick-Reference.md

File metadata and controls

2684 lines (1845 loc) · 109 KB

JavaScript Icon JavaScript Simplified: An Efficient Learning Guide

JavaScript Version License: MIT Contributions Welcome

A comprehensive yet concise quick-reference and overview of JavaScript fundamentals. Designed as both an efficient learning tool and a rapid reference. This guide bridges the gap between learning and practical application, making JavaScript more accessible.

Back to README

Introduction to JavaScript

Variables and Declarations

Fundamental Programming Concepts

Global JavaScript Objects

DOM & DOM Manipulation

Asynchronous JavaScript

Working with APIs

Object-Oriented Programming (OOP)

Building Projects with JavaScript

Tips and Best Practices


1.1 Overview of JavaScript

Explore the JavaScript language and its versatile capabilities, including:

  • Being a high-level, interpreted programming language.
  • Supporting dynamic typing for variables with varying data types.
  • Embracing multiple programming paradigms: event-driven, functional, and imperative.

JavaScript Core Features Core Features

Discover how JavaScript enhances user interaction on web pages by manipulating HTML and CSS. Learn how it handles asynchronous operations to make web applications more responsive.

JavaScript and ECMAScript JavaScript and ECMAScript

Understand JavaScript's adherence to ECMAScript (ES) standards, including significant updates introduced in ES6/ES2015, such as let/const, arrow functions, and classes.

JavaScript Environments Environments

Explore JavaScript's versatility in both client-side (browser) and server-side (Node.js) development. See how it empowers the creation of dynamic web page content and back-end applications.

JavaScript Role in Web Development Role in Web Development

Understand JavaScript's integral role in web development, including its interactivity, DOM manipulation, and server-side capabilities.

Integration with Web Technologies Integration with Web Technologies

Learn how JavaScript collaborates with HTML and CSS to deliver complete web page functionality. It utilizes AJAX alongside other modern methods for asynchronous tasks, such as Promises and Async/Await, to enhance interactivity and efficiency.

JavaScript Libraries and Frameworks Libraries and Frameworks

Explore libraries like React and frameworks like Angular, Vue.js, and Ember.js that simplify common tasks and provide robust tools for building complex applications.

1.2 JavaScript Code Order

The order of your code is vital for logical flow, efficient loading, and readability.

JavaScript Libraries and Frameworks Why It Matters

  • Logical Flow: Ensures proper function sequencing.
  • Loading Efficiency: Affects resource loading and page speed.
  • Readability: Enhances code comprehension.
  • Maintainability: Facilitates updates and bug fixes.
  • Collaboration: Simplifies teamwork and code sharing.

JavaScript Libraries and Frameworks Code Order Variability

Optimal code order varies based on project needs and best practices:

  • Library/Framework Integration: Follow library/framework guidelines (e.g., React prioritizes state and rendering).
  • Event-Driven Applications: In event-driven apps (e.g., games or chat), focus on event handling and async tasks.
  • Complex Form Validation: For intricate form validation, focus on form handling and validation early.
  • Performance Optimization: Adjust the order for performance optimization as per your app's requirements.
  • Team Workflow: Adapt to your team's workflow and code organization preferences when collaborating.

JavaScript Libraries and Frameworks General JavaScript Order

General structure to use as a baseline:

  1. DOM References and Global Variables: Declare and manage references to HTML elements and global variables.
  2. Modularization and Code Organization: Organize code into modules and understand import/export for better maintainability.
  3. Function Definitions and Local Variables: Define reusable functions and declare local variables within functions.
  4. Form Handling, Validation, and State Management: Efficiently handle forms, validate user input, and manage application state.
  5. Responsive Design and DOM Manipulations: Create responsive designs and interact with DOM elements.
  6. Event Listeners and Additional Initializations: Attach event listeners and perform setup tasks.
  7. Asynchronous Operations and API Calls: Handle asynchronous tasks and make API requests.
  8. Error Handling and Debugging: Implement error handling and debugging techniques.
  9. Performance Optimization and Testing: Optimize code for performance and introduce testing practices.

2.1 VARIABLES AND DECLARATIONS

Understanding variable declaration and scope in JavaScript.

  • 'var': Function-scoped variable declaration (uncommon in modern JS)

    • var oldVar = "I am old"; Avoid using 'var' when possible
  • 'let': Block-scoped variable declaration (can be reassigned)

    • let newLet = "I am new"; Preferred for variables that can change
  • 'const': Block-scoped and constant variable declaration (cannot be reassigned)

    • const constantVar = "I am constant"; Use for variables that should not change

2.2 BASIC DATA TYPES

Overview of primitive data types in JavaScript.

  • Number: Represents both integers and floats

    • let score = 75; Integer
    • let price = 9.99; Float
  • String: Represents textual data

    • let name = "Alice";
  • Boolean: Represents true or false values

    • let isPassed = score > 50;
  • Undefined: Variable declared but not assigned

    • let result;
  • Null: Explicitly signifies no value

    • let data = null;

2.3 SYNTAX AND EXAMPLES

Examples illustrating syntax for variable declarations and data types.

Declaring Multiple Variables

let x = 5, y = 10; // Multiple variables in one line

String Concatenation

let greeting = "Hello " + name; // "Hello Alice"

Template Literals (ES6)

`let greetingTemplate = `Hello ${name}`; // "Hello Alice"`

Arithmetic Operations

  • let sum = x + y; Addition (15)
  • let diff = y - x; Subtraction (5)

Boolean Logic

let isGreater = x > y; false

Type Coercion

let total = "3" + 4; "34", number 4 becomes a string

The typeof Operator

The typeof operator is used to determine the type of a variable or expression. It returns a string indicating the data type.

let num = 42
console.log(typeof num) // Outputs: "number"

let str = 'Hello, world!'
console.log(typeof str) // Outputs: "string"

let bool = true
console.log(typeof bool) // Outputs: "boolean"

2.4 Operators

Operators allow you to perform various tasks, such as assigning values to variables, comparing values, performing arithmetic operations, and more. This section provides an overview of the different types of operators in JavaScript.

Assignment Operators

  • = Assigns the value on the right to the variable on the left.
  • += Adds the value on the right to the variable on the left.
  • -= Subtracts the value on the right from the variable on the left.
  • *= Multiplies the variable on the left by the value on the right.
  • /= Divides the variable on the left by the value on the right.

Comparison Operators

  • == Checks if the values of two operands are equal.
  • != Checks if the values of two operands are not equal.
  • === Checks if the values and types of two operands are equal.
  • == Checks if the values or types of two operands are not equal.
  • > Checks if the value of the left operand is greater than the value of the right operand.
  • < Checks if the value of the left operand is less than the value of the right operand.
  • >= Checks if the value of the left operand is greater than or equal to the value of the right operand.
  • <= Checks if the value of the left operand is less than or equal to the value of the right operand.

Logical Operators

  • && Returns true if both operands are true.
  • || Returns true if either of the operands is true.
  • ! Returns true if the operand is false and false if the operand is true.

Arithmetic Operators

  • + Adds two operands.
  • - Subtracts the second operand from the first operand.
  • * Multiplies two operands.
  • / Divides the first operand by the second operand.
  • % Returns the remainder when the first operand is divided by the second operand

2.5 TYPE CONVERSION AND COERCION

In JavaScript, the conversion and interpretation of values between different types are critical concepts that enable the effective writing and understanding of code. This section focuses on explicit type conversion, also known as type casting, and implicit type coercion, providing insights into their mechanisms and use cases.

Explicit Type Conversion

Explicit type conversion, or type casting, involves converting values from one type to another using built-in functions, allowing for controlled and intentional data type changes.

  • String Conversion: To convert a value to a string, use the String() function. For example, String(123) converts the number 123 into the string "123".

  • Number Conversion: The Number() function is used to convert strings and other types to numbers. For instance, Number("123") turns the string "123" into the number 123. Inputs that cannot be converted to a number yield NaN (Not-a-Number).

  • Boolean Conversion: Using Boolean() converts values to booleans. Truthy values (values that are not 0, null, undefined, NaN, '', or false) convert to true, while falsy values convert to false.

Implicit Type Coercion

Implicit type coercion happens when JavaScript automatically converts types behind the scenes in the context of an operation.

  • Numeric String and Number: When a numeric string and a number are used in an operation, the string is coerced into a number. For example, "5" - 2 results in 3.

  • Boolean and Number: In arithmetic operations, true is treated as 1 and false as 0. Thus, true + 2 equals 3.

  • String Concatenation: When a number is added to a string, the number is converted into a string. For instance, 5 + "3" yields "53".

Understanding these conversion and coercion rules is essential for debugging and for writing clear, predictable JavaScript code.

// Explicit Type Conversion
console.log(String(123)); // "123" - Number to string
console.log(Number("123")); // 123 - String to number
console.log(Boolean(1)); // true - Number to boolean

// Implicit Type Coercion
console.log("6" - 2); // 4 - Numeric string and number in subtraction operation
console.log("6" + 2); // "62" - Number is converted to string in concatenation
console.log(true + false); // 1 - true is treated as 1, false as 0 in addition

// More examples
console.log("5" * "4"); // 20 - Strings coerced to numbers in multiplication
console.log("5" * true); // 5 - true is treated as 1 in multiplication
console.log(null + 1); // 1 - null is treated as 0 in addition
console.log(undefined + 1); // NaN - undefined is not converted in numeric operations

3.1 FUNCTIONS

In JavaScript, functions are fundamental building blocks, acting as callable objects to perform tasks or return values. They enhance code organization, reusability, and testability. This section delves into functions, covering declarations, parameters, and advanced concepts.

Function Declarations

Define functions using the function keyword, specifying parameters within parentheses.

function greet(name) {
  return `Hello, ${name}!`
}
console.log(greet('Alice'))

Outputs "Hello, Alice!"

Function Parameters and Arguments

Functions accept inputs called parameters, defined at declaration, and are placeholders for data passed during invocation, called arguments. Parameters are locally scoped within the function, allowing you to reference them even if they haven't been assigned outside. Additionally, you can reference external or existing parameters within the function body. Arguments, on the other hand, are the actual values passed to the function when it's called, allowing for dynamic operation based on the values provided.

// Variable declared outside the function
let globalVariable = 10

// Function that takes a parameter which is a variable declared outside the function
function multiplyByGlobal(globalVariable) {
  // Perform multiplication using the parameter and the global variable
  let result = userInput * globalVariable
  return result
}

// Example usage of the function
let userInput = 5
let multipliedValue = multiplyByGlobal(globalVariable)
console.log(`Result: ${multipliedValue}`) // Output: Result: 50

When to Use Parameters

  • Customization: Parameters enable functions to handle different scenarios by accepting varied inputs, enhancing flexibility. For instance, a function computing rectangle area can take length and width parameters for any rectangle.

  • Reusability: Parameterized functions are reusable, executing the same logic with diverse input values, enhancing code efficiency.

  • Abstraction: Parameters shield callers from implementation details, necessitating only knowledge of required inputs and outputs, fostering clearer interfaces.

  • Modularity: Parameterized functions promote code modularity, encapsulating specific tasks and inputs, facilitating comprehension and maintenance.

What can be used as Parameters

  • Local Variables: Scoped within the function, used for internal computations or temporary storage.

  • Global Variables: Can be used as parameters, but it's discouraged due to potential dependencies and decreased reusability.

  • Primitive Data Types: Accepted as parameters, they're copied when passed into the function (passed by value), ensuring the original value remains unchanged.

  • Complex Data Types: Objects and arrays can be passed as parameters, allowing modifications within the function to affect the original data (passed by reference).

  • Functions: Can be passed as parameters, enabling advanced programming techniques such as callbacks, higher-order functions, and functional programming paradigms.

Default Parameters (ES6)

Default parameters provide fallback values for function arguments if no value or undefined is passed when the function is called. They are specified in the function declaration and assigned using the syntax parameter = defaultValue.

function say(message = 'Hi') {
  console.log(message)
}
;`say();` // Outputs "Hi"
`say("Hello");` // Outputs "Hello"

Rest Parameters (ES6)

Rest parameters allow functions to accept an indefinite number of arguments as an array, providing a more flexible way to handle function arguments. They are represented by three dots (...) followed by the parameter name and collect multiple arguments into a single array parameter.

function sumAll(...numbers) {
  return numbers.reduce((acc, num) => acc + num, 0)
}
console.log(sumAll(1, 2, 3))

Outputs 6

Arrow Functions (ES6)

Arrow functions provide a concise syntax for writing function expressions in JavaScript, using the => syntax. They inherit the this value from the surrounding code context and are commonly used for short, one-liner functions or functions that don't require their own this context.

const arrowFunction = (parameters) => expression
// Traditional function expression to greet a user
function greetUser(userName) {
  return 'Hello, ' + userName + '!'
}

// Arrow function expression for greeting a user
const greetUserArrow = (userName) => `Hello, ${userName}!`

console.log(greetUser('John')) // Output: Hello, John!
console.log(greetUserArrow('Jane')) // Output: Hello, Jane!

In this example, the arrow function addArrow accomplishes the same thing but with a more concise syntax. It omits the function keyword and uses the => arrow syntax to define the function.

Function Expressions

Function expressions assign an anonymous function to a variable. They offer flexibility, allowing functions to be defined and passed as data. Within these function expressions, parameters can refer to both existing variables and parameters that have not been previously declared or defined. This allows for dynamic behavior based on the values passed to the function when it's invoked.

const square = function (x) {
  return x * x
}
console.log(square(4)) // Outputs 16

Comparing Function Types

// Function Declaration
function greetDeclaration(name) {
  return 'Hello, ' + name + '!'
}

// Function Expression
const greetExpression = function (name) {
  return 'Hello, ' + name + '!'
}

// Arrow Function
const greetArrow = (name) => {
  return 'Hello, ' + name + '!'
}

// Test the functions
console.log(greetDeclaration('Alice')) // Output: Hello, Alice!
console.log(greetExpression('Bob')) // Output: Hello, Bob!
console.log(greetArrow('Charlie')) // Output: Hello, Charlie!

IIFE (Immediately Invoked Function Expression)

IIFEs are functions that execute immediately upon definition, useful for initializing applications or namespaces.

;(function () {
  console.log('This function runs right away!')
})()

Higher-Order Functions

These functions accept or return other functions, facilitating abstraction and composition in programming.

function applyOperation(a, b, operation) {
  return operation(a, b)
}
;`const result = applyOperation(4, 2, multiply);` // Using the multiply arrow function
`console.log(result);` // Outputs 8

3.2 LOOPS

Loops enable repetitive execution of code, streamlining tasks like array traversal and conditional iterations.

Choosing the Right Loop Structure

  • for loop when you need to repeat a block of code for a known number of iterations, typically defined by a start condition, an end condition, and an increment.
  • while loop when the number of iterations is uncertain, and you need to continue iterating as long as a specific condition remains true.
  • do...while loop when you need to execute a block of code at least once before evaluating the loop condition for further iterations.
  • for...in loop to iterate over all enumerable properties of an object, often useful for iterating through object keys and values.
  • for...of loop (ES6) when iterating over iterable objects like arrays or strings, providing a simpler syntax without needing to deal with indexes or properties.

for Loop

Repeatedly runs a block of code a certain number of times.

for (let i = 0; i < 5; i++) {
  console.log(i) // Outputs: 0, 1, 2, 3, 4
}

while Loop

Executes code as long as a specified condition is true.

let j = 0
while (j < 5) {
  console.log(j) // Outputs: 0, 1, 2, 3, 4
  j++
}

do...while Loop

Executes code once, then repeats the loop as long as the condition is true.

let k = 0
do {
  console.log(k) // Outputs: 0, 1, 2, 3, 4
  k++
} while (k < 5)

for...in Loop

Iterates over all enumerable properties of an object.

const person = { name: 'Alice', age: 30 }
for (const key in person) {
  console.log(`${key}: ${person[key]}`) // Outputs: "name: Alice", "age: 30"
}

for...of Loop (ES6)

Iterates over iterable objects like arrays, strings.

const numbers = [1, 2, 3, 4, 5]
for (const number of numbers) {
  console.log(number) // Outputs: 1, 2, 3, 4, 5
}

Array.forEach()

Executes a specified function for each element within an array. While it's not a traditional loop, it's closely related to the topic of array iteration.

numbers.forEach((number) => {
  console.log(number) // Outputs: 1, 2, 3, 4, 5
})

3.3 CONDITIONAL STATEMENTS

Quick reference for using conditional logic in JavaScript.

if Statement

Executes code block if condition is true.

if (condition) {
  // Code to be executed if condition is true
}

if...else Statement

Executes one code block if condition is true, another if false.

if (condition) {
  // Code to be executed if condition is true
} else {
  // Code to be executed if condition is false
}

if...else if...else Statement

Executes multiple conditions.

if (condition1) {
  // Code if condition1 is true
} else if (condition2) {
  // Code if condition2 is true
} else {
  // Code if neither condition1 nor condition2 is true
}

switch Statement

Switch statements provide a way to perform different actions based on different conditions. They allow you to evaluate an expression and execute code blocks based on the matching case. Switch statements offer a more concise alternative to multiple if...else statements when dealing with multiple conditions.

switch (expression) {
  case value1:
    // Code block to execute if expression equals value1
    break
  case value2:
    // Code block to execute if expression equals value2
    break
  // Additional cases as needed
  default:
  // Code block to execute if expression doesn't match any case
}
const day = 'Monday'

switch (day) {
  case 'Monday':
    console.log('Today is Monday')
    break
  case 'Tuesday':
    console.log('Today is Tuesday')
    break
  default:
    console.log("It's neither Monday nor Tuesday")
}

Ternary Operator

The ternary operator is a concise way to write conditional statements in JavaScript. It takes three operands: a condition, an expression to execute if the condition is true, and an expression to execute if the condition is false.

condition ? expression1 : expression2
const age = 20
const status = age >= 18 ? 'adult' : 'minor'

console.log(status) // Outputs: 'adult' since age is greater than or equal to 18

3.4 ARRAYS

Detailed guide on JavaScript Arrays, covering array manipulation methods, iteration, and array-specific operations.

  • Use arrays to store a list of items in an ordered manner, where each item can be accessed by its numerical index.
  • Arrays are ideal for scenarios where you need to perform operations like looping through elements, as they provide methods for iteration and manipulation.

Properties

  • array.length Reflects the number of elements in an array.
  • array.prototype Represents the prototype for the Array constructor and allows to add new properties and methods to all Array objects.

Methods

  • array.from(arrayLike[, mapFn[, thisArg]]) Creates a new Array instance from an array-like or iterable object.
  • array.isArray(object) Returns true if a variable is an array, if not false.
  • array.of("element1", "element2"); Creates a new Array instance with a variable number of arguments, regardless of number or type of the arguments.

Mutator Methods

  • arr.copyWithin(target, start, end) Copies a sequence of array elements within the array.
  • arr.fill(value, start, end) Fills all the elements of an array from a start index to an end index with a static value.
  • arr.pop() Removes the last element from an array and returns that element.
  • arr.flat() Merges nested array into one single array.
  • arr.push("element1", "element2"); Adds one or more elements to the end of an array and returns the new length of the array.
  • arr.reverse() Reverses the order of the elements of an array in place — the first becomes the last, and the last becomes the first.
  • arr.shift() Removes the first element from an array and returns that element.
  • arr.sort() Sorts the elements of an array in place and returns the array.
  • array.splice(start, deleteCount, element1, element2) Adds and/or removes elements from an array.
  • arr.unShift("element1", "element2"); Adds one or more elements to the front of an array and returns the new length of the array.

Example of .sort() & .filter()

let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]

let result = numbers
  .sort((a, b) => a - b) // Sort in ascending order
  .filter((number) => number > 3) // Filter numbers greater than 3

console.log(result) // [4, 4, 5, 5, 5, 6, 9]

Accessor Methods

  • array.filter(callback[, thisArg]) Creates a new array containing elements that meet a specified condition defined by the provided callback function.
  • array.at(index) Returns the element at the specified index in the array.
  • array.concat(value1, value2, array2) Returns a new array comprised of this array joined with other array(s) and/or value(s).
  • array.includes(searchElement, fromIndex) Determines whether an array contains a certain element, returning true or false as appropriate.
  • array.indexOf(searchElement[, fromIndex]) Returns the first (least) index of an element within the array equal to the specified value, or -1 if none is found.
  • array.join(separator) Joins all elements of an array into a string.
  • array.lastIndexOf(searchElement, fromIndex) Returns the last (greatest) index of an element within the array equal to the specified value, or -1 if none is found.
  • array.slice(begin, end) Extracts a section of an array and returns a new array.
  • array.toString() Returns a string representing the array and its elements.
    • Overrides the Object.prototype.toString() method.
  • array.toLocaleString(locales, options) Returns a localized string representing the array and its elements.
    • Overrides the Object.prototype.toLocaleString() method.

Example of array.slice(begin, end)

// Suppose we have an array of months
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July']

// We want to get only the spring months
const springMonths = months.slice(2, 5)

console.log(springMonths) // Output: ['March', 'April', 'May']

Iterating Through Array Elements

When iterating through array elements, you can use the index notation [i] to access a specific element at index i:

const array = [10, 20, 30, 40, 50]

for (let i = 0; i < array.length; i++) {
  console.log(array[i]) // Accesses the element at index i
}

Iteration Methods

  • array.every(callback[, thisArg]) Returns true if every element in this array satisfies the provided testing function.
  • array.filter(callback[, thisArg]) Creates a new array with all of the elements of this array for which the provided filtering function returns true.
  • array.find(callback[, thisArg]) Returns the found value in the array, if an element in the array satisfies the provided testing function or undefined if not found.
  • array.findIndex(callback[, thisArg]) Returns the found index in the array, if an element in the array satisfies the provided testing function or -1 if not found.
  • array.forEach(callback[, thisArg]) Calls a function for each element in the array.
  • array.keys() Returns a new Array Iterator that contains the keys for each index in the array.
  • array.map(callback[, initialValue]) Creates a new array with the results of calling a provided function on every element in this array.
  • array.reduce(callback[, initialValue]) Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value.
  • array.reduceRight(callback[, initialValue]) Apply a function against an accumulator and each value of the array (from right-to-left) as to reduce it to a single value.
  • array.some(callback[, initialValue]) Returns true if at least one element in this array satisfies the provided testing function.
  • array.values() Returns a new Array Iterator object that contains the values for each index in the array.

Array.forEach()

Executes a specified function for each element within an array. While it's not a traditional loop, it's closely related to the topic of array iteration.

numbers.forEach((number) => {
  console.log(number) // Outputs: 1, 2, 3, 4, 5
})

array.reduce(callback[, initialValue])

// Suppose we have an array of numbers
const numbers = [1, 2, 3, 4, 5]

// We want to find the sum of all numbers in the array
const sum = numbers.reduce((accumulator, currentValue) => {
  return accumulator + currentValue
}, 0) // 0 is the initial value

console.log(sum) // Output: 15

Array.includes() method

The includes() method in JavaScript is used to determine whether an array includes a certain value among its elements. It returns true if the array contains the specified element, and false otherwise. This method offers a simple way to check for the presence of a value within an array without needing to iterate through its elements manually.

array.includes(searchElement, fromIndex)
const fruits = ['apple', 'banana', 'orange', 'grape']
console.log(fruits.includes('banana')) // Output: true
console.log(fruits.includes('watermelon')) // Output: false

Array Destructuring

Destructuring arrays provides a more concise and readable way to extract values from an array and assign them to variables. Leading to cleaner code and make it easier to work with arrays, especially when dealing with functions that return arrays or when handling array elements in different parts of your code.

  • Clarity and readability: Destructuring makes it clear which elements of the array you are interested in, improving code readability compared to accessing elements by index.
  • Conciseness: Destructuring allows you to extract multiple values from an array in a single statement, reducing the amount of code needed.
  • Variable naming: When destructuring, you can assign more meaningful variable names to the extracted values, making your code easier to understand.
  • Avoiding temporary variables: Destructuring can help you avoid the need for temporary variables when working with array elements, leading to cleaner code.
  • Function return values: Destructuring can be especially useful when dealing with functions that return arrays, allowing you to easily extract and work with the returned values.
// Define an array with numerical values
const numbers = [1, 2, 3]

// Destructure the array into individual variables
const [firstNumber, secondNumber, thirdNumber] = numbers

// Log the individual variables
console.log(firstNumber) // Output: 1
console.log(secondNumber) // Output: 2
console.log(thirdNumber) // Output: 3

3.5 OBJECTS

Objects are a collection of properties, where a property is an association between a name (or key) and a value. A property's value can be a function, in which case the property is known as a method.

  • Objects are used to store data as key-value pairs, allowing for the organization of data with named properties that can be accessed using keys.
  • They are suitable for representing more complex data structures where each item may contain a different set of properties.

What is a Key?

It is a unique identifier used to access the corresponding value in the object. Keys are often strings (but can be symbols in ES6 and beyond), representing property names.

What is a Value?

It is the data associated with a key within an object. Values can be of any data type, such as numbers, strings, arrays, functions, or even other objects.

what is a Key Value Pair?

Together, a key and its associated value form a key-value pair, allowing you to store and organize data in a structured way within an object. For example, in the following object,"name" and "age" are keys, while "John" and 30 are their corresponding values:

{ name: "John", age: 30 },

Creating an Object

  • const myObject = { key1: 'value1', key2: 'value2' }; Object literal with two properties
  • Object.create() creates a new object with a specified prototype object and properties

Accessing Properties

In JavaScript, objects are collections of properties, and you can access these properties using either dot notation or bracket notation.

  • console.log(myObject.key1); 'value1'
  • console.log(myObject['key2']); 'value2'

Dot Notation VS Bracket Notation

Dot notation is preferred for direct and straightforward property access when the property name is known at development time and is a valid JavaScript identifier. On the other hand, bracket notation offers more flexibility and versatility, allowing for dynamic property access, handling special cases, and enabling programmatic property access.

When to Use Dot Notation

  • Known Property Names: Use dot notation when you know the property name at development time and it is a valid JavaScript identifier.
  • Clear and Direct Access: Dot notation is ideal for accessing properties directly and offers a concise syntax for this purpose.
  • Code Readability: It enhances code readability, especially when accessing well-known properties of objects.

When to Use Bracket Notation

  • Dynamic Property Access: Use bracket notation when the property name is determined dynamically at runtime, such as when it's stored in a variable or computed through an expression.
  • Handling Special Cases: Bracket notation is essential for accessing properties with special characters, spaces, or reserved words in JavaScript.
  • Programmatic Property Access: It enables programmatic approaches to property access, making it useful for working with data structures, APIs, or situations where property names may vary.

Adding Properties

  • myObject.key3 = 'value3'; Adding a new property 'key3'

Deleting Properties

  • delete myObject.key2; Removing property 'key2'

Methods

A method in a JavaScript object is a property of the object that is a function. Methods are functions stored as object properties, and they are typically used to define actions or behavior for the object.

Structure for Creating and Using Methods

const exampleObject = {
  property1: 'Value1',
  property2: 'Value2',
  method1: function () {
    return 'Method1 returns: ' + this.property1
  },
  method2: function (parameter) {
    console.log(
      'Method2 uses ' + parameter + ' and property2: ' + this.property2
    )
  },
}

console.log(exampleObject.method1()) // Calls method1, outputs: Method1 returns: Value1
exampleObject.method2('Parameter1') // Calls method2 with "Parameter1" as an argument, outputs: Method2 uses Parameter1 and property2: Value2

Example of Creating and Using Methods

const person = {
  firstName: 'John',
  lastName: 'Doe',
  fullName: function () {
    return this.firstName + ' ' + this.lastName
  },
  greet: function (greeting) {
    console.log(greeting + ', my name is ' + this.firstName)
  },
}

console.log(person.fullName()) // Calls the fullName method, outputs: John Doe
person.greet('Hello') // Calls the greet method with "Hello" as an argument, outputs: Hello, my name is John

Methods of the Object Constructor

Methods of the Object constructor in JavaScript provide a set of utility functions for creating, manipulating, and working with objects, including methods for object creation, property manipulation, and property enumeration.

  • Object.assign(target, ...sources) Copies values from source to target objects.
  • Object.create(proto, propertiesObject) Creates a new object with the specified prototype and properties.
  • Object.defineProperty(obj, prop, descriptor) Defines a new property on an object.
  • Object.defineProperties(obj, props) Defines multiple properties on an object.
  • Object.entries(obj) Returns an array of a given object's own enumerable string [key, value] pairs.
  • Object.freeze(obj) Freezes an object, preventing new properties and changes to existing properties.
  • Object.getOwnPropertyDescriptor(obj, prop) Gets the descriptor for a property.
  • Object.getOwnPropertyDescriptors(obj) Gets all own property descriptors of an object.
  • Object.getOwnPropertyNames(obj) Returns an array of all properties (enumerable or not).
  • Object.getOwnPropertySymbols(obj) Returns an array of all symbol properties.
  • Object.getPrototypeOf(obj) Returns the prototype of the object.
  • Object.is(value1, value2) Compares if two values are the same.
  • Object.isExtensible(obj) Checks if an object can be extended with new properties.
  • Object.isFrozen(obj) Checks if an object is frozen.
  • Object.isSealed(obj) Checks if an object is sealed.
  • Object.keys(obj) Returns an array of a given object's own enumerable properties.
  • Object.preventExtensions(obj) Prevents any extensions of an object.
  • Object.seal(obj) Prevents other code from deleting properties of an object.
  • Object.setPrototypeOf(obj, prototype) Sets the prototype (i.e., the internal [[Prototype]] property).
  • Object.values(obj) Returns an array of a given object's own enumerable property values.

Example of object.assign()

// Create a target object
const target = {
  name: 'John',
  age: 30,
}

// Create source objects
const source1 = {
  age: 25,
  profession: 'Engineer',
}

const source2 = {
  city: 'New York',
  hobby: 'Guitar',
}

// Use Object.assign to copy values from sources to the target
Object.assign(target, source1, source2)

// Display the modified target object
console.log(target)

Looping Through Properties Using for...in

for...in loops through an object's properties to access and work with each property and its associated value.

for (const key in myObject) {
  console.log(key, myObject[key]) // Logs key and value of each property
}

Object Destructuring

Destructuring objects in JavaScript allows you to extract properties from objects and assign them to variables, providing a concise and readable way to work with object data. This feature enhances code clarity and reduces redundancy, especially when dealing with functions that return objects or when accessing object properties in different parts of your code.

  • Clarity and readability: Destructuring makes it explicit which properties of the object you are interested in, improving code readability compared to accessing properties by their keys.
  • Conciseness: Destructuring allows you to extract multiple properties from an object in a single statement, reducing the verbosity of your code.
  • Variable naming: When destructuring, you can assign more descriptive variable names to the extracted properties, enhancing code comprehension.
  • Avoiding repetition: Destructuring helps you avoid repeating object property access, leading to cleaner and more maintainable code.
  • Function return values: Destructuring can be particularly useful when dealing with functions that return objects, enabling you to easily extract and utilize the returned properties.
// Define an object with properties
const person = {
  firstName: 'John',
  lastName: 'Doe',
  age: 30,
}

// Destructure the object into individual variables
const { firstName, lastName, age } = person

// Log the individual variables
console.log(firstName) // Output: John
console.log(lastName) // Output: Doe
console.log(age) // Output: 30

3.6 ERROR HANDLING

Overview of error handling mechanisms in JavaScript.

try...catch Statement

Handles exceptions by testing a block of code for errors.

try {
  // Code that may throw an error
} catch (error) {
  console.log(error) // Handling the error
}

try...catch...finally Statement

Includes a block that runs regardless of the result.

try {
  // Code that may throw an error
} catch (error) {
  console.log(error) // Handling the error
} finally {
  // Code that will run regardless of try / catch outcome
}

throw Statement

Creates a custom error.

function checkNumber(num) {
  if (isNaN(num)) {
    throw new Error('Input is not a number') // Custom error
  }
}

Example Usage of throw

try {
  checkNumber('A')
} catch (e) {
  console.log(e.message) // Output: Input is not a number
}

4.1 STRINGS

Overview of commonly used methods of the String object.

Properties

  • str.length Returns the length of the string.
  • str.[index] Allows you to access characters in the string using bracket notation

Methods

  • str.charAt(index) Returns the character at the specified index.
  • str.charCodeAt(index) Returns an integer between 0 and 65535 representing the UTF-16 code unit at the given index.
  • str.concat(string2, string3[, ..., stringN]) Concatenates the string arguments to the calling string and returns a new string.
  • str.includes(searchString, position) Determines whether one string may be found within another string, returning true or false.
  • str.indexOf(searchValue[, fromIndex]) Returns the index within the calling String object of the first occurrence of the specified value, or -1 if not found.
  • str.lastIndexOf(searchValue[, fromIndex]) Returns the index within the calling String object of the last occurrence of the specified value, or -1 if not found.
  • str.match(regexp) Used to match a regular expression against a string.
  • str.repeat(count) Constructs and returns a new string which contains the specified number of copies of the string on which it was called, concatenated together.
  • str.replace(searchFor, replaceWith) Used to find a match between a regular expression and a string, and to replace the matched substring with a new substring.
  • str.search(regexp) Executes a search for a match between a regular expression and this String object.
  • str.split(separator[, limit]) Splits a String object into an array of strings by separating the string into substrings.
  • str.substr(start[, length]) Returns the characters in a string beginning at the specified location through the specified number of characters.
  • str.substring(indexStart[, indexEnd]) Returns a new string containing the specified part of the given string.
  • str.toLowerCase() Returns the calling string value converted to lowercase.
  • str.toUpperCase() Returns the calling string value converted to uppercase.
  • str.trim() Trims whitespace from the beginning and end of the string.
  • str.valueOf() Returns the primitive value of a String object.

String Interpolation (Template Literals)

  • const name = "Alice";
  • `const greeting = `Hello, ${name}!`;` Hello, Alice!

Multi-line Strings using Template Literals

const multiLineString = `This is a string
that spans across
multiple lines`
console.log(multiLineString)

4.2 NUMBERS

Fundamental number-related functionalities in JavaScript.

What is an Integer?

An integer is a whole number without any fractional or decimal parts, such as -1, 0, or 42. It's used in scenarios requiring countable quantities, like loop counters, array indexes, or any situation where partial values don't make sense.

What is a Floating-Point Number?

A floating-point number includes decimal parts, allowing for the representation of fractions and precise measurements, like 3.14 or -0.01. Useful in calculations requiring high precision, such as financial transactions, scientific computations, or any scenario where small, incremental values matter.

parseInt(string, radix)

The parseInt() function in JavaScript is used to convert a string to an integer (whole number). It takes two parameters:

  • The string to be converted (which should represent a number)
  • The base (or radix) of the numerical system to which the string belongs

When using parseInt(), it's crucial to specify the radix to ensure accurate conversion.

  • For decimal numbers (the usual number system), use base 10.

  • For binary numbers (0s and 1s), use base 2.

  • Decimal (Base 10): parseInt("10", 10); - Converts the string "10" to an integer in the decimal numerical system (base 10).

  • Binary (Base 2): parseInt("10", 2); - Converts the string "10" to an integer in the binary numerical system (base 2).

parseInt Example

// Sample input from the user
const userInput = '42'

// Using parseInt to convert the string to an integer
const parsedNumber = parseInt(userInput)

// Checking if the conversion was successful
if (!isNaN(parsedNumber)) {
  console.log(`Parsed number: ${parsedNumber}`)
  console.log(`Type of parsedNumber: ${typeof parsedNumber}`)
} else {
  console.log('Conversion failed. Please enter a valid number.')
}

parseFloat(string)

parseFloat("3.14"); Converts the string "3.14" to a floating-point number.

toFixed(digits)

const num = 123.456
num.toFixed(2) // Returns "123.46" - formats the number using fixed-point notation.

Array Destructuring

const fruits = ['Apple', 'Banana', 'Cherry']
const [firstFruit, secondFruit] = fruits
console.log(firstFruit) // 'Apple'
console.log(secondFruit) // 'Banana'

Spread Operator in Arrays

const primeNumbers = [2, 3, 5]
const morePrimes = [7, ...primeNumbers, 11]
console.log(morePrimes) // [7, 2, 3, 5, 11]

Additional Number Properties and Methods

  • Number.isFinite(1000); true
  • Number.isNaN(NaN); true
  • Number.isInteger(10); true
  • Number.isSafeInteger(10); true
  • Number.MAX_VALUE; The largest positive representable number
  • Number.MIN_VALUE; The smallest positive representable number

4.3 MATH

Essential guide to JavaScript's Math object, covering basic constants and mathematical functions.

Math Constants

  • Math.PI; π: Approximately 3.14159
  • Math.E; Euler's constant, e: Approximately 2.718

Basic Math Methods

  • Math.abs(-5); Absolute value: Returns 5
  • Math.ceil(4.2); Ceiling: Rounds up to 5
  • Math.floor(4.8); Floor: Rounds down to 4
  • Math.round(4.5); Round: Rounds to 5
  • Math.max(1, 3, 5); Maximum value: Returns 5
  • Math.min(1, 3, 5); Minimum value: Returns 1
  • Math.trunc(5.4); Removes the decimal (.4): Returns 5

Exponential and Logarithmic Functions

  • Math.exp(1); e^1: Returns Euler's number raised to the power of 1
  • Math.log(10); Natural logarithm of 10

Trigonometric Functions (Basic)

  • Math.sin(0) Sine: Returns 0 (sin of 0 degrees)
  • Math.cos(Math.PI); Cosine: Returns -1 (cos of 180 degrees)

Power and Square Root Functions

  • Math.pow(2, 3); 2 to the power of 3: Returns 8
  • Math.sqrt(16); Square root of 16: Returns 4

4.4 DATES

Introduction to JavaScript's Date object, focusing on creating, manipulating, and formatting dates.

Creating Date Objects

  • const now = new Date(); Current date and time
  • const specificDate = new Date('2024-01-24'); Specific date (YYYY-MM-DD)
  • const specificDateTime = new Date('2024-01-24T12:00:00'); Specific date and time (YYYY-MM-DDTHH:MM:SS)

Date Methods

  • now.getFullYear(); Get the year as a four digit number (yyyy)
  • now.getMonth(); Get the month as a zero-based value (0-11)
  • now.getDate(); Get the day as a number (1-31)
  • now.getDay(); Get the weekday as a number (0-6)
  • now.getHours(); Get the hour (0-23)
  • now.getMinutes(); Get the minute (0-59)
  • now.getSeconds(); Get the second (0-59)
  • now.getMilliseconds(); Get the milliseconds (0-999)
  • now.getTime(); Get the time (milliseconds since January 1, 1970)

Setting Date Values

  • now.setFullYear(2024); Set the year (optionally month, day)
  • now.setMonth(0); Set the month (0-11)
  • now.setDate(24); Set the day as a number (1-31)
  • now.setHours(12); Set the hour (0-23)
  • now.setMinutes(30); Set the minute (-59)
  • now.setSeconds(30); Set the second (0-59)
  • now.setMilliseconds(123); Set the milliseconds (0-999)

Formatting Dates

  • now.toDateString(); Converts the date portion to a readable string
  • now.toTimeString(); Converts the time portion to a readable string
  • now.toLocaleDateString(); Returns the date portion in a locale-sensitive format
  • now.toLocaleTimeString(); Returns the time portion in a locale-sensitive format
  • now.toISOString(); Returns the date in ISO format (YYYY-MM-DDTHH:MM:SS.sssZ)

Comparing Dates

  • const earlier = new Date('2024-01-01');
  • const later = new Date('2024-12-31');
  • const isLater = later > earlier; true if 'later' is a later date than 'earlier'

4.5 RANDOMNESS WITH Math.random()

Math.random() is a powerful JavaScript function that generates a pseudo-random number between 0 (inclusive) and 1 (exclusive). This function is widely used in various programming scenarios, from simple tasks like randomizing UI elements to complex simulations and algorithms. Understanding how to effectively use Math.random() can add a dynamic and unpredictable element to your applications.

Generating a Random Number

const randomNumber = Math.random()
console.log(randomNumber) // Any number between 0 and 1

Selecting a Random Item from an Array

Often, you need to pick an item randomly from a list. Here's how you can do it:

const items = ['Apple', 'Banana', 'Cherry', 'Date']
const randomIndex = Math.floor(Math.random() * items.length)
const randomItem = items[randomIndex]
console.log(randomItem) // Randomly selected item

Generating an integer with Math.random()

When it comes to generating a random integer between two values you can use either Math.trunc() or Math.floor(). The distinction between the two only matters for negative numbers, as Math.random() always returns a positive number.

Math.trunc

  • Math.trunc can be paired with Math.random() for generating zero-based index values, useful in selecting random elements from an array. For example, Math.trunc(Math.random() * array.length) can randomly index into an array, ensuring the index starts at 0 and is within the array bounds.

Math.floor

  • Math.floor, on the other hand, can be utilized with Math.random() to create inclusive upper-bound random integers. For instance, Math.floor(Math.random() * (max - min + 1)) + min generates a random integer between min and max, inclusively, catering to situations where the starting integer is 1 and the upper limit must be part of the outcome range.

Advanced Applications

Math.random() can be adapted for more complex scenarios, enhancing its utility beyond simple random selection.

Randomizing Within a Range

To generate a random number within a specific range, you can modify the output of Math.random():

function getRandomInRange(min, max) {
  return Math.random() * (max - min) + min
}
console.log(getRandomInRange(1, 100)) // Random number between 1 and 100

Weighted Random Selection

In situations where options have different probabilities of being chosen, a weighted random selection algorithm can be implemented:

function weightedRandom(options) {
  let i,
    sum = 0,
    r = Math.random()
  for (i in options) {
    sum += options[i]
    if (r <= sum) return i
  }
}
const choices = { A: 0.1, B: 0.3, C: 0.6 }
console.log(weightedRandom(choices)) // "C" has a higher chance of being selected

Dynamic Randomness

Math.random() can be particularly useful in scenarios where the data is dynamic or the exact values are not known in advance. For example, in game development for spawning items at random locations or in simulations where random factors influence outcomes.

// Function to simulate rolling a six-sided die
function rollDice() {
  // Generate a random number between 1 and 6
  let result = Math.floor(Math.random() * 6) + 1

  return result
}

// Example usage
console.log('Rolling the dice...')
let rollResult = rollDice()
console.log('The result is:', rollResult)

5.1 UNDERSTANDING DOM & DOM MANIPULATION

The DOM (Document Object Model) is a JavaScript programming interface for web documents. It represents web pages as a tree of nodes and objects, allowing you to change their structure, style, and content.

The DOM Basics

  • Introduction to the DOM: The DOM is a way to interact with web documents using JavaScript. It represents the page as a tree, enabling you to modify its content and structure.

  • window Object: The window object represents the browser window and provides functions and properties for window control and event handling.

  • document Object: The document object represents the web page's content. It allows JavaScript to add, modify, and interact with elements on the page.

Dom Manipulation Process

To manipulate the DOM, you begin by selecting the desired DOM element(s) using methods like getElementById, querySelector, or getElementsByClassName. Once you've selected the element(s), you can directly apply modifications, such as changing content or styling. While it's common to assign elements to variables for convenience and reusability, it's not mandatory. You can select elements and perform modifications without the need for intermediate variable declarations.

Example: Basic interaction with the DOM using JavaScript

// Access an element by its ID
const element = document.getElementById('example-element')

// Modify the element's content
element.innerHTML = 'This is the new content.'

// This updates the HTML content of the element.

5.2 ACCESSING AND MODIFYING DOM ELEMENTS

Methods for selecting, creating, and modifying DOM elements.

Primary Methods of Accessing Elements

  • document.getElementById(id); Gets an element by its ID.
  • document.querySelector(selector); Returns the first element matching the specified CSS selector.
  • document.querySelectorAll(selector); Returns a NodeList of all elements matching the specified CSS selector.

Other Methods of Accessing elements

  • document.getElementsByTagName(name); Returns a live HTMLCollection of elements with the given tag name.
  • document.getElementsByClassName(className); Returns a live HTMLCollection of elements with the given class name.

Creating and Inserting Elements

  • const newElement = document.createElement(tagName); Creates a new element with the specified tag name.
  • element.appendChild(newElement); Appends the new element as the last child of the parent element.

Removing Elements

  • element.removeChild(child); Removes a child node from the DOM.

Modifying Elements

  • element.innerHTML = '<p>New HTML content</p>'; Changes the HTML content of an element.
  • element.textContent = 'New text content'; Changes the text content of an element.
  • element.setAttribute(name, value); Sets a new value for an attribute on the element.
  • element.getAttribute(attributeName); Gets the current value of an attribute on the element.
  • element.removeAttribute(attributeName); Removes an attribute from the element.
  • element.classList.add(className); Adds a class to the element.
  • element.classList.remove(className); Removes a class from the element.
  • element.classList.toggle(className); Toggles a class on the element.
  • element.style.property = "value"; Changes the style of an element.

Methods For Acessesing Various HTML Element Types

  • <input> .value, .disabled, .checked
  • <div> .innerText, .innerHTML, .textContent, .style
  • <span> .innerText, .innerHTML, .textContent, .style
  • <p> .innerText, .innerHTML, .textContent, .style
  • <a>(Anchor): .innerText, .innerHTML, .textContent, .href, .target, .style
  • <button> .innerText, .innerHTML, .textContent, .disabled, .style
  • <img>(Image): .src, .alt, .style
  • <select>(Dropdown): .value, .selectedIndex, .options
  • <textarea> .value, .disabled, .style
  • <ul> (Unordered List): .innerHTML, .textContent, .style
  • <li> (List Item): .innerText, .innerHTML, .textContent, .style
  • <table> .innerHTML, .textContent, .style
  • <tr> (Table Row): .innerHTML, .textContent, .style
  • <td> (Table Data/Cell): .innerText, .innerHTML, .textContent, .style
  • <form> .submit(), .reset(), .addEventListener('submit', ...), addEventListener('reset', ...)

Examples of Common Style Properties & Attributes

  • element.style.color = "red"; Sets the text color of the element.
  • element.style.backgroundColor = "yellow"; Sets the background color of the element.
  • element.style.border = "1px solid black"; Sets a border for the element.
  • element.style.marginTop = "10px"; Sets the top margin of the element.
  • element.style.paddingLeft = "20px"; Sets the left padding of the element.
  • element.style.opacity = "0.8"; Adjusts element opacity.
  • element.style.boxShadow = "2px 2px 5px #888888"; Adds a box shadow.
  • element.style.transform = "rotate(45deg)"; Applies a rotation transform.
  • element.style.transition = "all 0.3s ease-in-out"; Sets transition effects.
  • element.style.zIndex = "100"; Controls the stacking order.
  • element.hidden = true; Hides the element.
  • element.disabled = true; Disables the element.

Cloning Elements

const clone = element.cloneNode(true); Clones the element and its descendants.

5.3 DOM NODE PROPERTIES AND METHODS

Overview of properties and methods specific to DOM nodes.

Node Properties

  • node.childNodes; A live NodeList containing all the children of this node.
  • node.firstChild; The node's first child in the tree, or null if the node has no children.
  • node.lastChild; The node's last child in the tree, or null if the node has no children.
  • node.nextSibling; The node immediately following this node, or null if there's no sibling node.
  • node.nodeName; The name of the node, depending on its type.
  • node.nodeType; A code representing the type of the node (e.g., 1 for Element nodes, 3 for Text nodes).
  • node.nodeValue; The value of the current node (varies depending on the node type).
  • node.parentNode; The parent of the node, or null if it has no parent.
  • node.previousSibling; The node immediately preceding this node, or null if there's no sibling.

Node Methods

  • node.cloneNode(deep); Clones the node. If 'deep' is true, it clones all descendants, otherwise it clones only the node.
  • node.contains(otherNode); Returns true if 'otherNode' is a descendant of the node, false otherwise.
  • node.hasChildNodes(); Returns true if the node has any children, false otherwise.
  • node.insertBefore(newNode, referenceNode); Inserts 'newNode' before the 'referenceNode' as a child of the current node.
  • node.replaceChild(newChild, oldChild); Replaces 'oldChild' with 'newChild' among the children of the node.

Example Usage

const list = document.getElementById('myList')

// Get the text content of the first child node
const firstChildText = list.firstChild.innerText

// Change the HTML content of the list
list.innerHTML = '<li>New Item 1</li><li>New Item 2</li>'

// Create a new list item element
const newItem = document.createElement('li')
newItem.innerText = 'New Item 3'

// Insert the new item at the beginning of the list
list.insertBefore(newItem, list.firstChild)

5.4 DOM EVENT HANDLING

Methods for attaching, handling, and removing event listeners on DOM elements.

Adding Event Listeners

Anonymous Function Event Listener

For simple, one-time event handling without the need for additional checks or complex logic:

document.getElementById('myButton').addEventListener('click', function () {
  handleButtonClick()
})

Named Function Event Listener

For additional checks, complex logic, or reusability, using a named function offers better code organization and maintainability:

document
  .getElementById('myButton')
  .addEventListener('click', buttonClickFunction)

DOMContentLoaded Event Listener

To execute code when the DOM is fully loaded and parsed, use the DOMContentLoaded event.

document.addEventListener('DOMContentLoaded', function () {})

Removing Event Listeners

  • elementName.removeEventListener("click", buttonClickFunction);

Event Propagation: Capturing and Bubbling

  • Capturing: Events propagate from the window down to the target's ancestors.
  • Bubbling: Events propagate from the target up to the window.

Stopping Event Propagation

function eventHandler(event) {
  event.stopPropagation() // Prevents further propagation of the current event.
}

Preventing Default Event Behavior

element.addEventListener('click', function (event) {
  event.preventDefault() // Cancels the event if it is cancelable, without stopping its propagation.
})

Common Event Types

  • Mouse Events: 'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'
  • Keyboard Events: 'keydown', 'keypress', 'keyup'
  • Form Events: 'submit', 'change', 'focus', 'blur'
  • Window Events: 'load', 'resize', 'scroll', 'unload', 'error'

Example of a keydown event

document.addEventListener('keydown', function (event) {
  if (event.key === 'Enter') {
    // Call your function here
    // Example: handleEnterKey();
  }
})

6.1 ASYNCHRONOUS OPERATIONS IN JAVASCRIPT

This section covers JavaScript's asynchronous operations, including network requests and user interactions.

What Are Asynchronous Operations?

Asynchronous operations are tasks that occur independently of the main program flow. They often involve actions like fetching data from a remote server, reading files, or waiting for user interactions. These tasks can take time to complete, and they may not block the execution of other parts of your program.

Common Approaches

There are several approaches for handling asynchronous operations in JavaScript:

  • Callbacks (Traditional Approach): Callbacks are a common way to manage asynchronous tasks. They involve passing a function as an argument to another function to execute when the asynchronous operation is complete.

  • Promises (Structured Approach): Promises provide a more structured way to handle asynchronous operations. They represent a value that may be available now or in the future, allowing you to perform actions once the operation completes.

  • Async/Await (Modern Approach): Async/await is a set of JavaScript keywords that simplifies working with promises. It allows you to write asynchronous code that resembles synchronous code, improving code readability and maintainability.

AJAX (Asynchronous JavaScript and XML)

AJAX enables dynamic client-server communication without needing to reload the page. It can be implemented using callbacks, promises, or async/await, making it a versatile technique for fetching data and updating the UI asynchronously. With the rise of modern APIs like Fetch, AJAX continues to be an integral part of creating interactive web applications.

6.2 CALLBACKS

A callback is a function passed as an argument to another function, executed once a specific task is complete. While useful in certain scenarios, callbacks have limitations, particularly in complex code with error handling.

When to Consider Using Callbacks

Callbacks are commonly used in the following situations:

  • Handling asynchronous operations, like fetching data from a server.
  • Managing event-driven programming, such as user interactions or timers.
  • Dealing with I/O operations, like reading files in Node.js.

While callbacks can be employed in these scenarios, they may not always be the most efficient or maintainable choice. In more complex applications, Promises or async/await can provide a more structured and readable approach.

Potential Downsides of Callbacks

  • Callback Hell: In complex applications with multiple nested callbacks, code readability can suffer, leading to a pattern known as "Callback Hell" or "Pyramid of Doom."

  • Limited Error Handling: Error handling can be more challenging with callbacks, often requiring the passing of error parameters to handle errors effectively.

Example: Fetching Data from a Server

// Simulated asynchronous data fetching
function fetchDataFromServer(callback) {
  // Simulate a network request delay
  setTimeout(() => {
    const responseData = { message: 'Data from the server' }
    callback(responseData)
  }, 2000) // Simulated 2-second delay
}

// Callback function to handle the fetched data
function handleFetchedData(data) {
  console.log('Received data:', data.message)
}

// Usage: Fetch data from the server and handle it with the callback
fetchDataFromServer(handleFetchedData)

6.3 PROMISES

Promises are a crucial part of JavaScript for handling asynchronous operations in a structured and organized manner.

What Are Promises?

Promises are a structured way to manage asynchronous operations in JavaScript. They represent a value that can become available now or in the future, allowing you to take action once the operation finishes.

Promises offer a clear separation between initiating an asynchronous task and dealing with its outcome or errors. They provide a standardized approach to asynchronous code, improving code clarity and maintainability.

Note: Promises have a capital first letter, unlike most JavaScript syntax.

Key Concepts of Promises

To understand promises fully, consider these key concepts:

  • Creation: You can create a promise using the Promise constructor, encapsulating an asynchronous task.

  • States: Promises have three states: pending, resolved (fulfilled), and rejected. They transition from pending to either resolved or rejected based on the outcome of the asynchronous task.

  • Handling: Promises offer methods like then() and catch() to handle the result or errors once the promise settles.

  • Chaining: Promises allow you to chain multiple asynchronous operations together, making it easy to sequence tasks.

Example: Creating and Consuming a Promise

// Simulated asynchronous data fetching using a Promise
function fetchDataFromServer() {
  return new Promise((resolve, reject) => {
    // Simulate a network request delay
    setTimeout(() => {
      const responseData = { message: 'Data from the server' }
      resolve(responseData) // Resolve the Promise with the fetched data
    }, 2000) // Simulated 2-second delay
  })
}
// Usage: Fetch data from the server using the Promise
fetchDataFromServer()
  .then((data) => {
    console.log('Received data:', data.message)
  })
  .catch((error) => {
    console.error('Error:', error)
  })

6.4 ASYNC/AWAIT

Async/await simplifies the process of working with promises and is widely adopted in modern JavaScript development.

What is async/await?

async/await is a set of JavaScript keywords that simplifies the process of working with promises, allowing you to write asynchronous code that resembles synchronous code, improving code readability and maintainability.

Benefits of async/await

  • Simplicity: async/await reduces the complexity of handling promises, resulting in more concise and readable code.
  • Error Handling: It simplifies error handling with try...catch blocks, improving code reliability.
  • Sequencing: async/await allows you to sequence asynchronous tasks in a natural order, enhancing code flow.

Using asnyc/await to Fetch Data From a Server

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    throw an Error('Failed to fetch data');
  }
}

Consuming Data Using the async Function

fetchData()
  .then((data) => {
    console.log(data) // Handle the fetched data
  })
  .catch((error) => {
    console.error(error.message) // Handle errors
  })

6.5 AJAX

AJAX stands for Asynchronous JavaScript and XML. It's a web development technique for creating interactive and dynamic web applications by enabling web pages to asynchronously communicate with the server. This means that it's possible to update parts of a web page without reloading the whole page, leading to a smoother user experience.

What is XML?

XML, which stands for Extensible Markup Language, is a markup language that defines a set of rules for encoding documents in a format that is both human-readable and machine-readable. It is primarily used to facilitate the sharing of structured data across different information systems, particularly via the internet.

Key Features of XML

  • Structured Data: XML provides a structured format for documents with a hierarchical arrangement of items, similar to HTML. However, unlike HTML, which is designed to display data, XML's primary purpose is to transport and store data.

  • Custom Tags: XML allows the definition of custom tags, enabling the creation of a document structure that suits the specific data being handled. This flexibility makes it suitable for a wide range of data representation needs.

  • Self-descriptive: An XML document is self-descriptive; it not only contains the data but also labels that describe the data's structure or semantics.

  • Widespread Use: XML is used in many aspects of web development, including web services (SOAP), RSS feeds for content distribution, and AJAX (for asynchronous data transfer).

Example of an XML Document

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend!</body>
</note>

Core Concepts of AJAX

  • Asynchronous Communication: AJAX allows the browser to communicate with the server asynchronously, without needing a page reload. This improves the user experience by making web applications feel more responsive and faster.

  • XMLHttpRequest (XHR) Object: Traditionally, AJAX has used the XMLHttpRequest object to interact with servers. It can send and receive information in various formats, including JSON, XML, HTML, and text files.

  • Fetch API: Modern web development has seen a shift towards using the Fetch API, a more powerful and flexible alternative to XMLHttpRequest. The Fetch API uses Promises, making it a better fit for the modern async/await pattern in JavaScript.

When to Use AJAX

  • Partial Page Updates: AJAX is perfect for updating portions of a page without needing to reload it entirely. This is ideal for scenarios where you need to dynamically update the content, such as refreshing a news feed or updating a user dashboard.
  • Form Submission: Use AJAX to enhance the user experience by submitting forms without refreshing the page. This allows for immediate feedback to the user, such as error messages or confirmation notices, without disrupting the flow of interaction.
  • Interactive Features: AJAX shines in applications that require quick interactions with the server, like providing live search results, auto-saving user inputs, or filtering data dynamically. It helps in keeping the application responsive and interactive without full page reloads.

When Not to Use AJAX

  • Real-time Communication: For applications that need a constant flow of data in real-time, such as chat applications or live sports updates, WebSockets or Server-Sent Events (SSE) are more appropriate. These technologies provide a more efficient, bi-directional communication channel between the client and server.
  • Complex Data Handling: If your application involves handling complex data operations or streaming large amounts of data, like video or audio streams, WebSockets or the Fetch API with streaming capabilities offer better performance and flexibility than AJAX.

General Guidelines

  • Performance and Scalability: Consider AJAX for small to medium-sized updates where the ease of implementation outweighs the need for real-time data exchange. For scenarios requiring high performance and scalability, especially with real-time data, look towards WebSockets.
  • Browser Support and Compatibility: While AJAX is widely supported across modern browsers, ensure compatibility and consider polyfills for older browsers if necessary. Similarly, assess the browser support for alternative technologies like WebSockets and the Fetch API.
  • Development Complexity: AJAX can be a simple solution for straightforward data fetching and updates. For more complex, interactive, and real-time applications, investing in WebSockets or utilizing the Fetch API might be more suitable despite the initial complexity.

Example: Using Fetch API for AJAX Calls

// Function to fetch data from the server using the Fetch API
function fetchData(url) {
  fetch(url)
    .then((response) => response.json()) // Convert the response to JSON
    .then((data) => console.log(data)) // Handle the data
    .catch((error) => console.error('Error fetching data:', error)) // Handle any errors
}

// Example usage
fetchData('https://api.example.com/data')

6.6 WEBSOCKETS

WebSockets enable real-time, bidirectional communication between the client and server, offering a persistent connection that enhances user experiences in applications requiring live interactions.

Understanding WebSockets vs. Traditional Asynchronous Methods

While callbacks, promises, and async/await facilitate asynchronous operations in JavaScript, they typically operate within the request-response paradigm of HTTP. WebSockets transcend this by maintaining an open, ongoing communication line, allowing for instantaneous data exchange without the overhead of HTTP's start-stop nature.

Use Cases

  • Live chat applications
  • Real-time notifications
  • Multiplayer online games
  • Financial trading platforms
  • Collaborative editing tools

How It Works

  1. Opening a Connection: Initiated by a WebSocket upgrade request from the client, which the server accepts, upgrading the connection from HTTP.

  2. Data Transfer: Data is sent back and forth in real-time with minimal overhead.

  3. Closing the Connection: The connection can be closed by either the client or server at any time.

Example Usage

// Connect to a WebSocket server
const socket = new WebSocket('ws://example.com');

// Listen for messages from the server
socket.onmessage = event => {
  console.log('Message from server:', event.data);
};

// Send a message to the server
socket.send('Hello, server!');

// Close the connection
socket.close();

7.1 UNDERSTANDING APIs & JSON

Exploring the fundamentals of APIs, JSON and the importance of API keys in web development.

APIs at a Glance

APIs (Application Programming Interface) serve as foundational elements in modern development, enabling the seamless exchange of data between different applications, platforms, and services. Through APIs, developers can collect, transfer, and manipulate data in standardized formats such as JSON (JavaScript Object Notation). This allows for efficient communication and integration across diverse systems, facilitating tasks like data collection, processing, and presentation.

API Keys

For many APIs, you'll need an API key for authentication and usage tracking:

  1. Sign Up: Create an account on the API provider's website.
  2. Generate Key: In your account settings, generate an API key.
  3. Usage Guidelines: Check the provider's documentation for usage instructions, rate limits, and any costs.

JSON Simplified

JSON (JavaScript Object Notation) is a lightweight data-interchange format that is widely used for transmitting data between a server and a web application, and for storing data. JSON is a text-based format and is language-independent, meaning it can be easily parsed and generated by various programming languages, not just JavaScript.

In JavaScript, JSON data is displayed as objects using key-value pairs enclosed in curly braces {} or it can also represent arrays, which are ordered collections of values enclosed in square brackets [].

Handling JSON

When working with JSON data, remember to:

  • Parse: Convert JSON to JavaScript objects.
  • Access: Find specific information within the structure.
  • Modify: Adjust data as needed.
  • Stringify: Convert JavaScript objects back to JSON for storage or transmission.

7.2 API KEY MANAGEMENT

API Key Management involves securely storing keys server-side and regulating their use to prevent unauthorized access, crucial for protecting applications from security breaches.

API Key Management: Educational Examples vs. Real-World Application

This guide displays code examples of API requests that don't use API key management for learning ease. Securing API keys is more advanced, requiring additional tools and knowledge in server-side development. However, secure management is vital in real-world applications for data security and integrity.

When It's important to follow API Key Management

  • Production Environments: Exposing API keys can lead to unauthorized access and potential data breaches.

  • Handling Sensitive Data: When dealing with personal, financial, or confidential information, securing API keys is essential to prevent data theft and service abuse.

  • Rate Limits and Costs: Exposed keys can be misused, leading to rate limit breaches and unexpected costs.

Securing API Keys - Advanced Approach

  • Environment Variables: Use server-side environment variables for API keys. Tools like dotenv in Node.js can help manage these.

  • Server-Side Requests: Process API requests on the server. This requires setting up a server environment (Node.js, Python, etc.) and understanding server-side programming.

  • Access Control: Utilize API provider's settings to limit key usage by IP, referer URL, or application.

  • Rate Limiting and Monitoring: Apply rate limiting and monitor API key usage to detect unauthorized access. This may involve additional monitoring tools or services.

7.3 MAKING API REQUESTS

Using Fetch() API facilitates asynchronous data fetching, enhancing user experience by ensuring that interactions are not blocked during data retrieval.

Fetch API using Async/Await (WithoutAPI Key)

// DOM elements initialization
const userInput = document.getElementById('userInput')
const dataContainer = document.getElementById('dataContainer')
const errorMessage = document.getElementById('errorMessage')
const searchButton = document.getElementById('searchButton')

// Function to fetch data from API asynchronously
async function fetchData() {
  try {
    // Encode user input for URL parameter
    const encodedInput = encodeURIComponent(userInput.value)

    // Fetch request to a public API without API key requirement
    const response = await fetch(
      `https://api.example.com/data?search=${encodedInput}`
    )

    // Check response status
    if (!response.ok) {
      // Throw error for unsuccessful response
      throw new Error(`HTTP error! Status: ${response.status}`)
    }

    // Parse response to JSON
    const data = await response.json()

    // Display the entered user input
    userInput.textContent = `User Input: ${userInput.value}`

    // Extract values from response
    const value1 = data.property1 // Use actual data property names
    const value2 = data.property2 // Use actual data property names

    // Update DOM with response data
    dataContainer.textContent = `Value 1: ${value1}, Value 2: ${value2}`
  } catch (error) {
    // Handle and display error
    errorMessage.textContent = `Error: ${error.message}`
  }
}
// Attach an event listener to the search button
searchButton.addEventListener('click', fetchData)

Fetch API using Async/Await (With API Key)

// DOM element references
const userInput = document.getElementById('userInput')
const dataContainer = document.getElementById('dataContainer')
const errorMessage = document.getElementById('errorMessage')
const searchButton = document.getElementById('searchButton')

// API key (note: secure it properly in real-world apps)
const apiKey = 'YOUR_API_KEY_HERE'

// Async function to fetch data from API
async function fetchData() {
  try {
    // Encode user input for URL inclusion
    const encodedInput = encodeURIComponent(userInput.value)

    // Fetch data from API with encoded input
    const response = await fetch(
      `https://api.example.com/data?api_key=${apiKey}&search=${encodedInput}`
    )

    // Check for successful response
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`)
    }

    // Parse JSON response
    const data = await response.json()

    // Display user input
    userInput.textContent = `User Input: ${userInput.value}`

    // Extract and display values from response
    const value1 = data.property1 // Adjust property names
    const value2 = data.property2 // Adjust property names

    dataContainer.textContent = `Value 1: ${value1}, Value 2: ${value2}`
  } catch (error) {
    // Handle and display errors
    errorMessage.textContent = `Error: ${error.message}`
  }
}

// Attach an event listener to the search button
searchButton.addEventListener('click', fetchData)

7.4 HANDLING JSON DATA

Handling JSON data in JavaScript involves parsing JSON strings into JavaScript objects and serializing JavaScript objects into JSON strings. Understanding the correct sequence and the factors that affect it is crucial for effective data manipulation.

Sequence and Factors Affecting Sequence

The sequence of operations in handling JSON data is essential for smooth data management. Factors such as the source of data, its intended use, and any required transformations dictate the order of operations. By adhering to this sequence and considering these factors, developers ensure efficient and reliable JSON data management in their applications.

  1. Parsing JSON Data:

    • Use JSON.parse() to convert a JSON string into a JavaScript object.
    • This step is typically performed when receiving data from an external source, such as an API response or reading from a file.
  2. Converting JavaScript Objects to JSON:

    • Utilize JSON.stringify() to convert a JavaScript object into a JSON-formatted string.
    • This is commonly done when preparing data for storage or transmission, such as sending it to a server or saving it locally.
  3. JSON Data Manipulation:

    • Access and modify JSON data as needed, using JavaScript object notation.
    • Operations like accessing nested properties, adding or removing elements, and updating values are performed directly on the JavaScript object.

Parsing JSON Data

const jsonString = '{"name": "John", "age": 30, "city": "New York"}'

try {
  const jsonData = JSON.parse(jsonString)
  const result = jsonData // Use 'result' for further processing
} catch (error) {
  console.error('JSON Parsing Error:', error)
}

Converting JavaScript Objects to JSON

const person = {
  name: 'Alice',
  age: 25,
  city: 'San Francisco',
}

const jsonPerson = JSON.stringify(person)
const result = jsonPerson // Use 'result' for further processing

JSON Data Manipulation

const data = {
  users: [
    { id: 1, name: 'John' },
    { id: 2, name: 'Alice' },
    { id: 3, name: 'Bob' },
  ],
}

// Accessing JSON Data
const userName = data.users[0].name
const result1 = userName // Use 'result1' for further processing

// Modifying JSON Data
data.users.push({ id: 4, name: 'Eve' })

// Converting Back to JSON
const updatedJsonData = JSON.stringify(data)
const result2 = updatedJsonData // Use 'result2' for further processing

This sequence ensures smooth handling of JSON data, from parsing to manipulation and serialization. Adjustments may be made based on specific requirements and data flow within your application.

8.1 OBJECT-ORIENTED PROGRAMMING (OOP) IN JAVASCRIPT

OOP is a programming paradigm that focuses on organizing code into objects that encapsulate data and behavior. In JavaScript, OOP is supported through the use of objects, classes (introduced in ES6), and prototypes.

In JavaScript, arrays and objects (including instances of classes) are both considered objects in terms of the programming paradigm. However, they serve different purposes: while both objects and arrays are considered objects in JavaScript, they serve different purposes within the context of Object-Oriented Programming. Arrays are commonly used for handling ordered collections of data, while objects are used to represent entities with properties and behaviors. JavaScript is considered an OOP language because it supports the creation and manipulation of objects, including classes and inheritance, in addition to arrays.

Objects

Objects are instances of classes or constructors and encapsulate data and behavior. They consist of properties (data) and methods (functions).

// Creating an Object
const person = {
  name: 'John',
  age: 30,
  sayHello: function () {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  },
}

// Accessing Object Properties
console.log(person.name) // Output: "John"

// Calling Object Methods
person.sayHello() // Output: "Hello, my name is John and I'm 30 years old."

Classes

Classes are blueprints or templates for creating objects. They define the structure and behavior of objects.

// Creating a Class
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  sayHello() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  }
}

// Creating Objects from a Class
const person1 = new Person('Alice', 25)
const person2 = new Person('Bob', 35)

// Calling Class Methods
person1.sayHello() // Output: "Hello, my name is Alice and I'm 25 years old."
person2.sayHello() // Output: "Hello, my name is Bob and I'm 35 years old."

Constructor Method

The constructor method is called when an object is created from the class. It initializes object properties.

Class Inheritance

Classes can inherit properties and methods from another class using the'extends' keyword. This promotes code reusability and the creation of class hierarchies.

// Inheritance Example
class Animal {
  constructor(name) {
    this.name = name
  }

  speak() {
    console.log(`${this.name} makes a sound.`)
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name) // Calls the parent class constructor
    this.breed = breed
  }

  speak() {
    console.log(`${this.name} (a ${this.breed} dog) barks.`)
  }
}

const dog1 = new Dog('Buddy', 'Golden Retriever')

Polymorphism

Polymorphism allows objects of different classes to be treated as objects ofa common superclass. It enables flexibility and dynamic behavior based on the specific object type.

Polymorphism Example

const animals = [new Dog('Buddy', 'Golden Retriever'), new Animal('Kitty')]

for (const animal of animals) {
  animal.speak()
}

Output

  • "Buddy (a Golden Retriever dog) barks."
  • "Kitty makes a sound."

Object Composition

In JavaScript, you can create objects by composing or combining multiple objects, allowing for more flexible and modular code.

const address = {
  street: '123 Main St',
  city: 'Exampleville',
  zipCode: '12345',
}

const person = {
  name: 'John Doe',
  age: 30,
  address: address, // Object composition
}

9.1 USING HTML ELEMENTS IN JAVASCRIPT

JavaScript enhances web pages by interacting with HTML elements to create dynamic content and user interfaces. Understanding the role of different HTML elements and their use cases in JavaScript is crucial for effective web development. This section provides a concise overview of common HTML elements used in JavaScript and their intended applications.

<div>

  • Description: Serves as a generic container for flow content, typically used to group elements for styling purposes or to control layout using CSS.
  • Use Case: Use when you need a container for dynamic content manipulation or as a layout tool.

<span>

  • Description: An inline container used to mark up a part of a text, or a part of a document.
  • Use Case: Ideal for styling small portions of text or for applying JavaScript actions to inline elements without disrupting document flow.

<button>

  • Description: Represents a clickable button.
  • Use Case: Use for triggering actions or events, like submitting forms or interactive content changes.

<input>

  • Description: Allows the user to enter data. Various types include text, radio, checkbox, etc.
  • Use Case: Use for creating interactive controls for forms to gather user input.

<form>

  • Description: Represents a document section containing interactive controls for submitting information.
  • Use Case: Use when you need to collect user input or submissions.

<label>

  • Description: Represents a caption for an item in a user interface.
  • Use Case: Use to associate text with form elements, improving accessibility and user interaction.

<select>

  • Description: Allows the selection from a list of options.
  • Use Case: Use when you need to provide a dropdown menu of choices to the user.

<option>

  • Description: Defines an option in a select list.
  • Use Case: Use to provide individual choices inside a <select> element.

<textarea>

  • Description: Allows multi-line text input.
  • Use Case: Use when you need to allow users to enter larger amounts of text, such as comments or feedback.

<a>

  • Description: Defines a hyperlink, which can be used to link from one page to another.
  • Use Case: Use for navigation purposes or linking to external documents, often with JavaScript to enhance functionality without changing pages.

<ul>, <ol>, <li>

  • Description: Represent unordered (<ul>), ordered (<ol>) lists, and list items (<li>), respectively.
  • Use Case: Use for displaying a list of items dynamically. JavaScript can dynamically add, remove, or reorder list items.

9.2 CHOOSING BETWEEN OBJECTS AND ARRAYS

In JavaScript, both objects and arrays are used to store collections of data. However, choosing the right type of data structure for your data can significantly impact the readability, efficiency, and simplicity of your code. This section aims to provide guidance on when to use objects and when to use arrays.

Understanding Objects

Objects are key-value pairs where each key is a string and the value can be anything. They are ideal for storing data with named properties.

When to Use Objects

  • Descriptive Data: Use objects when you need to store data with descriptive properties. For example, user profiles, where each property (name, age, email) describes the user.
  • Unique Keys: Use objects when each entry has a unique key, and you might need to look up data based on those keys.
  • Variable Properties: Use objects when the properties of your data might vary from one item to another.

Example:

const userProfile = {
  name: 'John Doe',
  age: 30,
  email: 'johndoe@example.com',
}

Understanding Arrays

Arrays are ordered collections of values. They are best suited for storing lists of items where order matters.

When to Use Arrays

  • Ordered Data: Use arrays when the order of your data is important. For example, a list of tasks in a to-do list application.
  • Homogeneous Elements: Use arrays when you're dealing with a collection of similar items, where each item doesn't need a named key.
  • Simple List of Values: Use arrays for simple lists where you primarily need to iterate over values or access them by index.

Example:

const taskList = ['Buy groceries', 'Call Alice', 'Read a book']

Making the Choice

  • Complexity and Structure: Use objects when your data is structured with multiple properties. Arrays are simpler and best for lists of items.
  • Access Pattern: Use objects for direct access via keys and arrays for sequential access.
  • Performance Considerations: Accessing elements by index in an array is generally faster than accessing values of an object by keys.

Choosing between objects and arrays depends on the nature of the data you're dealing with and how you plan to use it. Understanding these differences allows you to write more efficient, readable, and maintainable JavaScript code.

Visual Comparison of an Object Vs. an Array

// Basic Object
const person = {
  name: 'John',
  age: 30,
  city: 'New York',
}

console.log(person.name) // Output: John
console.log(person.age) // Output: 30
console.log(person.city) // Output: New York

// Basic Array
const numbers = [1, 2, 3, 4, 5]

console.log(numbers[0]) // Output: 1
console.log(numbers[2]) // Output: 3
console.log(numbers.length) // Output: 5

9.3 MANAGING USER INPUT

Handling user input effectively is crucial for creating interactive and user-friendly web applications. JavaScript provides several methods and practices to manage user input efficiently, ensuring data integrity and enhancing user experience. This section covers useful tips and techniques for managing user input in fundamental JavaScript, focusing on aspects other than form validation.

Why Convert User Input to the Correct Data Format?

When dealing with user input, it's important to ensure that the data is in the correct format for processing. Converting user input to the correct data format is essential for the following reasons:

  • Data Integrity: Converting user input to the correct data format helps maintain data integrity by ensuring consistency and accuracy.
  • Data Validation: Properly formatted data makes it easier to validate and perform checks on user input, reducing the risk of errors or unexpected behavior.
  • Comparison and Calculations: Different types of data require different handling. For example, you cannot directly compare strings to numbers or perform mathematical calculations on strings. Converting user input to the appropriate data type enables correct comparisons and calculations.

How to Convert User Input to the Correct Data Format?

JavaScript provides various methods for converting user input to the correct data format:

  • .parseInt(), .parseFloat(), or Number(): Convert string input to numbers for numeric calculations or comparisons.

  • .toLowerCase() or .toUpperCase(): Standardize string input for case-insensitive comparisons.

  • .trim(): Remove leading and trailing whitespace from string input to ensure data consistency.

  • Converting String Input to Numbers:

const userInput = document.getElementById('quantity').value
const quantity = parseInt(userInput, 10)

Standardizing String Input for Comparison:

const userInput = document.getElementById('searchQuery').value.toLowerCase()

Removing Whitespace from String Input:

const userInput = document.getElementById('username').value.trim()

Differences Between Internal Calculations and Display Logic

When managing user input, it's crucial to distinguish between internal calculations and display logic:

  • Internal Calculations: User input, after conversion to the appropriate data format, is often utilized for internal calculations or data manipulation within the application, such as arithmetic operations or logical evaluations.

  • Display Logic: Data intended for presentation to the user may require additional formatting or processing before rendering. This includes tasks like converting timestamps into user-friendly date formats or updating visual elements to reflect changes in input data.

  • Updating DOM Content: Dynamically updating DOM content in response to user input is essential for providing a responsive and interactive user experience. This ensures that the displayed information accurately reflects the user's actions, enhancing usability and engagement.

    • innerText: Used to retrieve or set the text content of an element without interpreting HTML. It's commonly used for updating text-based content dynamically.

    • innerHTML: Retrieves or sets the HTML content of an element, allowing for more complex updates including HTML markup.

    • setAttribute: Sets an attribute of an HTML element, providing a way to dynamically modify element attributes such as href or src.

    • classList: Manipulates the classes of an element, enabling dynamic addition, removal, or toggling of CSS classes for styling or behavior changes.

    • style: Allows for dynamic manipulation of CSS styles of an element, enabling changes such as color, size, or position.

    • value: Gets or sets the value of form elements like input fields, allowing for dynamic interaction and retrieval of user-entered data.

9.4 FORM VALIDATION

Form validation is a critical aspect of web development, ensuring that the data submitted by users meets specific criteria before being processed. Effective validation improves data quality, enhances security, and provides a better user experience by preventing errors and guiding users through the input process.

Client-Side Validation

Client-side validation occurs in the browser before the data is submitted to the server. It provides immediate feedback to users and reduces server load.

HTML5 Validation Attributes

  • Purpose: Leverage browser capabilities to perform basic validations without additional code.
  • Usage: Attributes like required, type="email", minlength, maxlength, and pattern.
  • Scenario: Use for simple validations such as required fields, email formats, and numerical constraints.

Example:

<input
  type="email"
  id="email"
  required
  pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$" />

JavaScript Custom Validation

  • Purpose: Implement complex or custom logic that cannot be achieved with HTML5 attributes alone.
  • Usage: JavaScript to create custom validation functions triggered on form events.
  • Scenario: Use for complex scenarios like password strength, input dependency checks, or asynchronous validations (e.g., checking username availability).

Example:

document.getElementById('form').addEventListener('submit', function (event) {
  const email = document.getElementById('email').value
  if (!email.match(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/)) {
    event.preventDefault()
    // Display an error message or highlight the input field
  }
})

Providing Feedback

  • Purpose: Communicate validation results to the user, highlighting errors or confirming correct inputs.
  • Usage: Dynamically update the DOM to display error messages or visual cues.
  • Scenario: Use immediately upon input (real-time feedback) or after submitting the form (summary feedback).

Example:

if (!isValidEmail(email)) {
  document.getElementById('email-error').innerText =
    'Please enter a valid email address.'
  event.preventDefault()
}

Separating form validation into its own section emphasizes its importance in web development. By implementing both client-side validations with HTML5 and custom JavaScript, developers can ensure data integrity, enhance user interaction, and prevent unnecessary server processing for invalid data.

9.5 PERSISTING DATA WITH WEB STORAGE

Web Storage allows web applications to store data locally within the user's browser, offering two main mechanisms: localStorage and sessionStorage. This section delves into practical applications of both, showcasing how they can be utilized in real-world web development scenarios.

Introduction to Web Storage

  • localStorage: Enables data storage across browser sessions. Data persists until explicitly cleared, making it suitable for storing user preferences or long-term data.
  • sessionStorage: Limits data storage to the lifetime of the page session. It's cleared when the tab is closed, ideal for temporary data like form inputs or session-specific data.

Example 1: Saving User Preferences with localStorage

Scenario: A website allows users to choose a theme. Using localStorage, the selected theme persists across sessions, providing a consistent user experience.

// Saving the selected theme
function saveTheme(theme) {
  localStorage.setItem('theme', theme)
}

// Applying the saved theme on page load
function applySavedTheme() {
  const theme = localStorage.getItem('theme')
  if (theme) {
    document.body.classList.add(theme)
  }
}

applySavedTheme()

Example 2: Storing Session Data with sessionStorage

Scenario: A multi-step form where user progress should not be lost if the page reloads, but doesn't need to persist beyond the current session.

// Saving form data to sessionStorage
function saveFormData(step, data) {
  sessionStorage.setItem(`formStep${step}`, JSON.stringify(data))
}

// Retrieving form data on page load to restore state
function retrieveFormData(step) {
  const data = sessionStorage.getItem(`formStep${step}`)
  return data ? JSON.parse(data) : null
}

Example 3: ShoppingCart Persistence

Scenario: An e-commerce site uses localStorage to persist the shopping cart contents even after the browser is closed, improving user experience by allowing users to return to their cart at a later time.

// Adding item to cart and saving to localStorage
function addToCart(item) {
  let cart = JSON.parse(localStorage.getItem('shoppingCart')) || []
  cart.push(item)
  localStorage.setItem('shoppingCart', JSON.stringify(cart))
}

// Loading the cart contents on page load
function loadCart() {
  const cart = JSON.parse(localStorage.getItem('shoppingCart')) || []
  // Code to render cart items goes here
}

Web Storage, encompassing both localStorage and sessionStorage, offers robust solutions for persisting data in web applications. By leveraging these technologies, developers can enhance the user experience, providing seamless session continuity and personalized settings across visits. Through practical applications like theme selection, form data preservation, and shopping cart management, Web Storage proves to be an invaluable asset in modern web development.

9.6 CODING LOGIC VS DATA RETRIEVAL & EXECUTION

Understanding the distinction between coding logic and data retrieval & execution in JavaScript is crucial for effective programming.

Coding Logic

Involves the core computational aspects such as algorithms, conditional statements, loops, and functions. It's about the internal logic that doesn't directly interact with the outside world (user interface, external APIs).

  • Mathematical calculations: Performing operations like addition, subtraction, multiplication, and division.
  • String manipulation: Operations like concatenation, slicing, or pattern matching.
  • Conditional checks: Making decisions based on certain conditions using if-else statements or switch cases.

Data Retrieval and Execution

Focuses on interacting with external data sources (APIs, databases) and the user interface. It involves fetching, displaying, and reacting to data. Includes fetching API data, handling user inputs, updating the web page dynamically.

  • Key Methods: fetch(), .addEventListener(), .innerHTML, console.log, alert.

return

Utilized within functions to output a value back to the caller. It serves as an internal mechanism for passing data between functions or parts of your code. The return statement does not produce any visible output in the user interface or console. It is purely for internal data flow within the application.

function functionName(parameters) {
  // Function body
  // Compute value or perform operations

  return value // Value to be returned
}

When to use various methods of data execution

  • console.log: Outputs debugging information to the browser's console, external to application logic and UI.
  • prompt: Displays dialogue box that prompts the user for text input.
  • alert: Displays a message to the user via a dialog box, directly interacting with the user, external to code logic.
  • .innerHTML/.innerText: Changes an element's HTML/Text content, directly modifying the web page's visible content.

10.1 TIPS AND BEST PRACTICES

Tips for cleaner, more efficient and maintainable JavaScript code.

1. Use Descriptive Variable and Function Names

  • Choose meaningful and descriptive names for variables and functions to improve code readability.
  • Avoid overly abbreviated or cryptic names.

2. Consistent Code Formatting

  • Follow a consistent code formatting style to enhance code maintainability.
  • Consider using popular coding conventions like the Airbnb JavaScript Style Guide.

3. Comment Your Code

  • Add comments to explain complex logic, important decisions, or the purpose of functions and variables.
  • Use comments to provide context to your code for yourself and others.

4. Avoid Global Variables

  • Minimize the use of global variables to prevent potential conflicts and improve code modularity.
  • Use local variables or encapsulate code within functions or modules.

5. Embrace ES6 Features

  • Familiarize yourself with ES6 (ECMAScript 2015) features and syntax enhancements.
  • Utilize features like arrow functions, template literals, and destructuring for cleaner code.

6. Error Handling

  • Implement proper error handling mechanisms, including try...catch blocks, to handle exceptions gracefully.

7. Optimize Loops

  • Be mindful of loop performance when dealing with large data sets.
  • Use efficient loop constructs and consider optimizations like loop unrolling.

8. Strict Mode

  • Strict mode is a feature in JavaScript that introduces stricter rules for writing JavaScript code.
  • It helps to prevent common coding errors and makes it easier to write secure JavaScript.
  • To enable strict mode, add the text 'use strict'; at the beginning of a script or a function body

9. Modular Code

  • Break down your code into reusable and modular components or functions.
  • Embrace the concept of modules and imports for better code organization.

10. Testing

  • Write unit tests to ensure the correctness of your code.
  • Explore testing frameworks like Jest, Mocha, or Jasmine.

11. Stay Updated

  • Keep up with the latest developments in JavaScript and web technologies.
  • Follow industry best practices and consider performance optimizations.

Further Learning Resources

🔝 Back to Top