Home Blog Page 155

Working with JSON and LocalStorage in JavaScript

0
full stack development
full stack development

Storing Data in the Browser and Handling JSON

JavaScript offers powerful mechanisms for storing data in the browser and exchanging it between applications. Two of the most commonly used tools for these tasks are JSON (JavaScript Object Notation) and LocalStorage. Understanding these tools will allow you to persist data, make API requests, and improve user experience.

In this module, we’ll explore how to use JSON for data exchange and LocalStorage for client-side storage.


Table of Contents

  1. What is JSON?
  2. JSON Syntax
  3. JSON.stringify() – Converting Objects to JSON
  4. JSON.parse() – Converting JSON to Objects
  5. Working with LocalStorage
  6. Limitations of LocalStorage
  7. Best Practices for Using LocalStorage
  8. JSON and API Communication
  9. Conclusion

1. What is JSON?

JSON stands for JavaScript Object Notation. It is a lightweight, text-based data format that is easy for humans to read and write, and easy for machines to parse and generate.

JSON is widely used for data exchange between a client and a server, especially in web APIs.

Example of a JSON object:

{
"name": "John Doe",
"age": 30,
"isStudent": false
}

2. JSON Syntax

JSON objects consist of:

  • Key-value pairs: The key is always a string, and the value can be a string, number, object, array, boolean, or null.
  • Arrays: JSON can hold ordered lists of values.

Example:

{
"person": {
"name": "Alice",
"age": 25
},
"isMember": true,
"hobbies": ["reading", "traveling"]
}

3. JSON.stringify() – Converting Objects to JSON

JSON.stringify() is used to convert a JavaScript object into a JSON string. This is useful when sending data to a server or storing it in a file.

Example:

const person = {
name: "Alice",
age: 25
};

const jsonString = JSON.stringify(person);
console.log(jsonString);
// Output: '{"name":"Alice","age":25}'

4. JSON.parse() – Converting JSON to Objects

JSON.parse() converts a JSON string back into a JavaScript object. This is useful when receiving data from a server or parsing a stored string.

Example:

const jsonString = '{"name":"Alice","age":25}';
const person = JSON.parse(jsonString);

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

Be careful when parsing invalid JSON, as it will throw an error.


5. Working with LocalStorage

LocalStorage allows you to store data persistently in the browser, even if the page is reloaded or the browser is closed. It stores key-value pairs as strings.

Storing Data:

localStorage.setItem("username", "JohnDoe");

Retrieving Data:

const username = localStorage.getItem("username");
console.log(username); // JohnDoe

Removing Data:

localStorage.removeItem("username");

Clearing All Data:

localStorage.clear();

6. Limitations of LocalStorage

While LocalStorage is useful for simple data storage, it has its limitations:

  • 5MB Storage Limit: Each domain can store up to 5MB of data.
  • Synchronous API: LocalStorage operations block the main thread.
  • String Storage: Everything is stored as a string, requiring manual parsing (e.g., JSON.parse and JSON.stringify).
  • No Expiry: Data stored in LocalStorage does not expire unless explicitly cleared.

7. Best Practices for Using LocalStorage

  • JSON Parsing and Stringifying: Always use JSON.parse() and JSON.stringify() to store and retrieve non-string data (objects, arrays, etc.) from LocalStorage.

Example:

const user = { name: "Alice", age: 25 };
localStorage.setItem("user", JSON.stringify(user));

// Retrieve the data and parse it back to an object
const storedUser = JSON.parse(localStorage.getItem("user"));
console.log(storedUser.name); // Alice
  • Data Size Considerations: Avoid storing large objects or arrays in LocalStorage. Use it only for small, essential data.
  • Error Handling: Always check if data exists before retrieving it. Handle cases where LocalStorage might be disabled in certain browsers.

Example:

const user = localStorage.getItem("user");
if (user) {
console.log(JSON.parse(user));
} else {
console.log("No user data found.");
}

8. JSON and API Communication

JSON is commonly used for communication between the client and server, especially in RESTful APIs.

Sending JSON to a Server:

const data = {
username: "Alice",
password: "password123"
};

fetch("https://api.example.com/login", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Error:", error));

Receiving JSON from a Server:

fetch("https://api.example.com/user")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Error:", error));

9. Conclusion

JSON and LocalStorage are foundational to modern JavaScript development. JSON enables easy data exchange between systems, while LocalStorage allows the browser to persist small amounts of data. By understanding how to handle these tools, you can improve user experience by storing user preferences, session data, and more.

Next up: Working with Asynchronous JavaScript: Promises and Async/Await

JavaScript Event Loop, Call Stack, and Concurrency Explained

0
full stack development
full stack development

Understanding How JavaScript Executes Code Asynchronously

If you’ve ever wondered how JavaScript handles multiple tasks—like fetching data while still responding to user interactions—this module is for you. To truly master asynchronous JavaScript, you must understand the call stack, event loop, callback queue, and microtask queue.

Let’s break it all down.


Table of Contents

  1. JavaScript: Single-Threaded but Non-Blocking
  2. The Call Stack
  3. Web APIs and Asynchronous Code
  4. The Callback Queue (Task Queue)
  5. The Event Loop
  6. Microtasks and the Job Queue
  7. Example: Sync vs Async Execution
  8. Visualizing the Event Loop
  9. Common Pitfalls and Interview Scenarios
  10. Conclusion

1. JavaScript: Single-Threaded but Non-Blocking

JavaScript is single-threaded, meaning it executes one command at a time in a main thread. But it’s also asynchronous and non-blocking, thanks to the browser’s or Node’s underlying event loop system.

This enables:

  • Non-blocking I/O
  • Smooth UI interactions
  • Async programming with callbacks, promises, and async/await

2. The Call Stack

The call stack is a data structure that keeps track of function calls. When a function is invoked, it’s pushed onto the stack. When it finishes, it’s popped off.

function greet() {
console.log("Hello!");
}

function start() {
greet();
}

start();

Call stack order:

  1. start()greet()console.log("Hello!")

3. Web APIs and Asynchronous Code

When you run something like setTimeout, it doesn’t block the stack. Instead, the browser or Node environment delegates it to Web APIs and offloads it.

console.log("Start");
setTimeout(() => {
console.log("Inside timeout");
}, 1000);
console.log("End");

Output:

Start
End
Inside timeout

The timeout’s callback waits outside the call stack, and the event loop pushes it in only when the stack is clear.


4. The Callback Queue (Task Queue)

When async operations like setTimeout, event listeners, or AJAX calls finish, their callbacks are sent to the task queue.

The event loop keeps checking:

  • Is the call stack empty?
  • If yes, move the oldest item from the task queue to the call stack.

5. The Event Loop

The event loop is a mechanism that coordinates the call stack and the queues.

Its job:

  • Keep the program running
  • Push async callbacks into the stack when ready
  • Maintain non-blocking behavior

Think of it as a traffic cop that decides when callbacks can run.


6. Microtasks and the Job Queue

Microtasks are higher-priority tasks that come from:

  • .then() of a Promise
  • queueMicrotask()

They are placed in the microtask queue, which is processed before the next task queue cycle.

console.log("1");

setTimeout(() => console.log("2"), 0);

Promise.resolve().then(() => console.log("3"));

console.log("4");

Output:

1
4
3
2

Because .then() runs before setTimeout, even with a delay of 0.


7. Example: Sync vs Async Execution

function syncExample() {
console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
console.log("D");
}

syncExample();

Output:

A
D
C
B

8. Visualizing the Event Loop

Imagine this:

  • Call Stack: Executes code line-by-line.
  • Web APIs: Wait for setTimeout, fetch, etc.
  • Callback Queue: Stores ready callbacks (setTimeout, DOM events).
  • Microtask Queue: Stores promise callbacks.
  • Event Loop: Moves tasks from the queues to the stack.

Order of Execution:

  1. Call stack
  2. Microtasks
  3. Next Task Queue

9. Common Pitfalls and Interview Scenarios

  • Starvation of task queue: Heavy microtasks can block task queue.
  • setTimeout(fn, 0) still runs after current stack + microtasks.
  • Deep nesting can overflow the call stack.
  • Misunderstanding async nature leads to bugs like:let result; fetchData().then(res => result = res); console.log(result); // undefined!

10. Conclusion

The Event Loop is at the core of how JavaScript handles concurrency and asynchronous code. Understanding this allows you to write non-blocking, performant applications, and ace those tricky JS interviews.

Next up: Working with JSON and LocalStorage

JavaScript Modules – import, export, and Code Organization

0
full stack development
full stack development

Modular JavaScript: Writing Clean, Maintainable, and Scalable Code

As JavaScript applications grow in size and complexity, organizing code into modules becomes essential. ES6 introduced a native module system that allows developers to break code into separate files and reuse it with clean import and export syntax.

In this module, we’ll explore what modules are, how they work, and best practices to use them effectively.


Table of Contents

  1. What Are Modules in JavaScript?
  2. Benefits of Using Modules
  3. export Statement
  4. import Statement
  5. Default Exports vs Named Exports
  6. Combining Named and Default Exports
  7. Modules in Browser vs Node.js
  8. Using Modules in HTML
  9. Module Scope
  10. Best Practices
  11. Conclusion

1. What Are Modules in JavaScript?

A module is a file that contains reusable code. Instead of putting everything in one file, you split logic across multiple files and import/export functionality as needed.


2. Benefits of Using Modules

  • Encourages code reuse
  • Improves readability and maintainability
  • Avoids global scope pollution
  • Enables lazy loading and better performance
  • Facilitates team collaboration

3. export Statement

To make something available outside a file, you use export.

// math.js
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;

You can export variables, functions, or classes.


4. import Statement

To use exported code in another file:

// main.js
import { add, multiply } from './math.js';

console.log(add(2, 3)); // 5
console.log(multiply(4, 5)); // 20

The file path must be relative and include the .js extension when used in browsers.


5. Default Exports vs Named Exports

Named Export (can export multiple things):

export const greet = () => console.log("Hi");
export const PI = 3.14;

Import like:

import { greet, PI } from './utils.js';

Default Export (only one per file):

// logger.js
export default function log(msg) {
console.log(msg);
}

Import like:

import log from './logger.js';

You can name the default import anything.


6. Combining Named and Default Exports

// lib.js
export const version = "1.0.0";
export default function sayHello() {
console.log("Hello!");
}
// main.js
import sayHello, { version } from './lib.js';

7. Modules in Browser vs Node.js

  • Browser: Use ES Modules with <script type="module">.
  • Node.js: Use import/export in .mjs files or enable "type": "module" in package.json.
<!-- index.html -->
<script type="module" src="main.js"></script>

Modules are deferred by default, so no need to add defer.


8. Using Modules in HTML

If you’re working with front-end JavaScript:

<!-- main.js -->
import { sayHi } from './greetings.js';
sayHi("Ravi");
<!-- index.html -->
<script type="module" src="main.js"></script>

Make sure your files are served over HTTP (not just opened with file://) due to CORS restrictions.


9. Module Scope

Every module has its own scope. Variables and functions defined inside a module are not accessible outside unless explicitly exported.

// helper.js
const secret = "12345"; // not exported
export const reveal = () => "The secret is hidden.";

10. Best Practices

  • Group related functions into modules.
  • Use default export for primary functionality and named exports for helpers.
  • Keep module files small and focused.
  • Avoid circular dependencies.
  • Use index files to simplify imports.

Example index.js:

export * from './math.js';
export * from './string.js';

Now import everything from one place:

import { add, capitalize } from './utils/index.js';

11. Conclusion

JavaScript modules bring structure and reusability to your codebase. They’re essential for building modern applications, and you’ll find them in every serious JavaScript project.

Next up: JavaScript Event Loop, Call Stack, and Concurrency Explained

Error Handling in JavaScript – Writing Robust and Safe Code

0
full stack development
full stack development

Understanding Errors and Exception Handling in JavaScript

No matter how carefully we write our code, errors are inevitable. Proper error handling is crucial to make applications stable, user-friendly, and easier to debug. JavaScript provides several powerful mechanisms for handling errors gracefully.

In this module, we’ll cover error types, handling techniques, and best practices.


Table of Contents

  1. Types of Errors in JavaScript
  2. What is Exception Handling?
  3. try…catch Statement
  4. The throw Keyword
  5. Error Object and Custom Errors
  6. finally Block
  7. Asynchronous Error Handling
  8. Debugging with DevTools
  9. Best Practices
  10. Conclusion

1. Types of Errors in JavaScript

There are three major categories of errors:

  • Syntax Errors – mistakes in code structure. console.log("Hello" // SyntaxError: missing )
  • Runtime Errors – occur during execution. let a = b + 2; // ReferenceError: b is not defined
  • Logical Errors – incorrect output, no error thrown. function isEven(n) { return n % 2 === 1; // wrong logic! }

2. What is Exception Handling?

Exception handling is a way to deal with unexpected events during program execution without crashing the entire app.


3. try…catch Statement

The try...catch block is used to catch and handle errors gracefully.

try {
let result = riskyFunction();
console.log(result);
} catch (error) {
console.error("Something went wrong:", error.message);
}
  • Code in the try block is executed.
  • If an error occurs, control jumps to the catch block.

4. The throw Keyword

You can throw custom errors using throw.

function divide(a, b) {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
}

try {
divide(5, 0);
} catch (e) {
console.error(e.message);
}

You can throw anything (strings, objects), but using the Error constructor is recommended.


5. Error Object and Custom Errors

Built-in Error Types:

  • Error
  • SyntaxError
  • ReferenceError
  • TypeError
  • RangeError

Creating Custom Errors:

class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}

throw new ValidationError("Invalid input");

6. finally Block

Code in finally always runs, regardless of whether an error occurred.

try {
// code
} catch (e) {
// handle error
} finally {
console.log("Cleanup actions here");
}

Useful for closing resources, stopping loaders, etc.


7. Asynchronous Error Handling

In async functions or Promises, errors must be caught with .catch() or try...catch in async/await.

Promises:

fetch("invalid-url")
.then(res => res.json())
.catch(error => console.error("Fetch failed:", error));

Async/Await:

async function loadData() {
try {
const res = await fetch("invalid-url");
const data = await res.json();
} catch (error) {
console.error("Error fetching:", error.message);
}
}

8. Debugging with DevTools

Modern browsers come with developer tools for debugging:

  • Use breakpoints to pause execution.
  • Watch variable values in real time.
  • View call stack and scopes.

Add debugger; in your code to trigger the debugger:

function test() {
let a = 5;
debugger;
console.log(a);
}

9. Best Practices

  • Always validate user inputs.
  • Avoid swallowing errors silently.
  • Use meaningful error messages.
  • Use try...catch wisely — not everywhere.
  • Prefer async/await with try...catch over .then().catch() for readability.
  • Log errors for diagnostics, especially in production.

10. Conclusion

Robust error handling is vital for building professional JavaScript applications. It not only prevents crashes but also helps developers debug and improve UX.

In the next module, we’ll dive into JavaScript Modules (import/export) — a powerful way to organize and reuse code.

Modern JavaScript – ES6+ Features You Should Know

0
full stack development
full stack development

Unlocking the Power of ES6+ in JavaScript

With the release of ECMAScript 2015 (commonly known as ES6) and subsequent updates (ES7 through ES2024+), JavaScript has evolved dramatically. These modern features make your code more readable, concise, and powerful. In this module, we’ll explore the most impactful and commonly used features introduced in ES6 and beyond.


Table of Contents

  1. Let and Const
  2. Arrow Functions
  3. Template Literals
  4. Destructuring
  5. Spread and Rest Operators
  6. Default Parameters
  7. Enhanced Object Literals
  8. For…of Loops
  9. Map and Set
  10. Classes and Inheritance
  11. Promises and Async/Await
  12. Optional Chaining and Nullish Coalescing
  13. Modules (import/export)
  14. Conclusion

1. let and const (Block Scoping)

These replace var and provide more predictable scoping.

let x = 10;
const y = 20;

x = 15; // ✅ allowed
// y = 25; ❌ Error: Assignment to constant variable

Use const by default unless reassignment is needed.


2. Arrow Functions

Shorter syntax and lexical this.

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

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

Arrow functions do not have their own this, arguments, or super.


3. Template Literals

Allows string interpolation and multiline strings.

const name = "Sysoi";
console.log(`Welcome to ${name} platform!`);

4. Destructuring

Extract values from arrays or objects easily.

const user = { name: "Ravi", age: 30 };
const { name, age } = user;

const arr = [1, 2, 3];
const [a, b] = arr;

5. Spread and Rest Operators

Spread (...) – expands values

const arr1 = [1, 2];
const arr2 = [...arr1, 3, 4]; // [1, 2, 3, 4]

Rest (...) – collects remaining items

function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}

6. Default Parameters

Assign default values to function arguments.

function greet(name = "Guest") {
console.log(`Hello, ${name}`);
}

7. Enhanced Object Literals

Simplify object creation:

let age = 25;
const user = {
name: "Amit",
age, // shorthand
greet() { // shorthand method
console.log("Hi!");
},
};

8. For…of Loop

Iterate over iterable objects like arrays:

for (let value of [1, 2, 3]) {
console.log(value);
}

Unlike for...in, this does not iterate over object properties.


9. Map and Set

Map – key-value pairs (any type as key)

const map = new Map();
map.set("name", "Ravi");
console.log(map.get("name"));

Set – unique values only

const set = new Set([1, 2, 2, 3]);
console.log(set); // Set {1, 2, 3}

10. Classes and Inheritance

Syntax sugar over prototype-based inheritance.

class Animal {
constructor(name) {
this.name = name;
}

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

class Dog extends Animal {
speak() {
console.log(`${this.name} barks`);
}
}

11. Promises and Async/Await

Promises:

fetch("https://api.example.com/data")
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));

Async/Await:

async function getData() {
try {
const res = await fetch("https://api.example.com/data");
const data = await res.json();
console.log(data);
} catch (err) {
console.error(err);
}
}

12. Optional Chaining & Nullish Coalescing

Optional Chaining (?.)

let user = {};
console.log(user?.address?.city); // undefined

Nullish Coalescing (??)

let name = null;
console.log(name ?? "Guest"); // "Guest"

13. Modules: import/export

Break code into reusable files.

// math.js
export const add = (a, b) => a + b;

// app.js
import { add } from './math.js';
console.log(add(2, 3));

14. Conclusion

ES6+ features make JavaScript more expressive, modern, and easier to manage. These are now the standard, so make sure you’re using them in your day-to-day development.

Next up: Error Handling in JavaScript – we’ll dive into try...catch, throw, custom error objects, and debugging tips.