What is slice() in JavaScript

What is slice()?

slice() is a method used to copy a portion of an array or string without changing the original.

Think of it like cutting a piece out of a cake — you get the piece, but the original cake stays the same.


Syntax

array.slice(start, end)
string.slice(start, end)
  • start → The index where the extraction begins (inclusive).

    • If omitted → starts from 0.

    • If negative → counts from the end.

  • end → The index where extraction stops (exclusive).

    • If omitted → goes till the end.

    • If negative → counts from the end.


Key Points

  1. Does not modify the original array/string.

  2. Returns a new array (or string in case of strings).

  3. Works with negative indexes.


Array Examples

1) Basic Usage

const fruits = ["apple", "banana", "cherry", "date"];
const sliced = fruits.slice(1, 3);

console.log(sliced);       // ["banana", "cherry"]
console.log(fruits);       // ["apple", "banana", "cherry", "date"] (unchanged)

Here:

  • Starts at index 1"banana".

  • Stops before index 3 → excludes "date".


2) Omitting the end

const colors = ["red", "green", "blue", "yellow"];
console.log(colors.slice(2)); // ["blue", "yellow"]

3) Using Negative Indexes

const numbers = [1, 2, 3, 4, 5];
console.log(numbers.slice(-3));     // [3, 4, 5]
console.log(numbers.slice(1, -1));  // [2, 3, 4]
  • -3 → starts from the 3rd last element.

  • end = -1 → stops before the last element.


4) Copying the Whole Array

const arr = [10, 20, 30];
const copy = arr.slice();

console.log(copy); // [10, 20, 30]
console.log(copy === arr); // false (different array in memory)

String Examples

const text = "JavaScript";
console.log(text.slice(0, 4));  // "Java"
console.log(text.slice(4));     // "Script"
console.log(text.slice(-6));    // "Script"

Difference between slice() and splice()

Feature slice() splice()
Changes original? ❌ No ✅ Yes
Return value New array with extracted items Removed items (also modifies the original array)
Usage Copy part of array/string Add/remove elements in array

Example:

const nums = [1, 2, 3, 4];
nums.slice(1, 3); // [2, 3]  (nums unchanged)
nums.splice(1, 2); // removes 2 elements starting at index 1 → nums becomes [1, 4]

Quick Cheat Sheet

  • Inclusive start index, exclusive end index.

  • Works with arrays and strings.

  • Negative index → counts from end.

  • Good for copying and non-destructive slicing.


For...of loop in JavaScript

What is for...of?

for...of is a loop that iterates over values of an iterable object.
An iterable is something that can be looped over, like:

  • Arrays

  • Strings

  • Maps

  • Sets

  • Typed Arrays

  • Generator objects

  • arguments object

Think of it as: “Give me each value in this collection, one at a time.”


Syntax

for (const value of iterable) {
  // use the value
}
  • iterable → something loopable (like an array or string)

  • value → each value from that iterable


Basic Example with Array

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

for (const fruit of fruits) {
  console.log(fruit);
}
// apple
// banana
// cherry

Here:

  • for...of directly gives values (not indexes)

  • Cleaner than a for loop if you don’t need the index.


Example with String

const word = "Hello";

for (const char of word) {
  console.log(char);
}
// H
// e
// l
// l
// o

It loops through each character.


Example with Map

const map = new Map([
  ["name", "Gagan"],
  ["role", "Developer"]
]);

for (const [key, value] of map) {
  console.log(key, "=>", value);
}
// name => Gagan
// role => Developer

Example with Set

const set = new Set([1, 2, 3, 3]);

for (const value of set) {
  console.log(value);
}
// 1
// 2
// 3   (no duplicates)

Comparing for...in vs for...of

Feature for...in for...of
Loops over Keys (property names) Values
Works on Objects (and arrays, but not ideal) Iterables (arrays, strings, maps…)
Use case Loop through object properties Loop through iterable values
Includes inherited properties? Yes No

Why use for...of?

✅ Simpler syntax for values
✅ Works naturally with iterables
✅ Avoids index-to-value conversion in arrays
✅ Cleaner than forEach if you want to use break or continue


When NOT to use for...of

  • On plain objects — they are not iterable by default:

const obj = { a: 1, b: 2 };
for (const value of obj) { // ❌ TypeError
  console.log(value);
}

Instead, convert to an array first:

for (const value of Object.values(obj)) {
  console.log(value);
}

Extra Tip — Getting index in for...of

for...of doesn’t give indexes directly, but you can use entries():

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

for (const [index, color] of colors.entries()) {
  console.log(index, color);
}
// 0 red
// 1 green
// 2 blue

Cheat Sheet

  • for...in → keys/properties

  • for...of → values of iterables

  • Arrays, strings, maps, sets → ✅ works with for...of

  • Plain objects → ❌ not iterable without conversion


For...in loop in JavaScript

What is for...in?

for...in is a loop that goes over the enumerable property keys (names) of an object. Each turn of the loop gives you a key (a string). You can use that key to access the value with bracket notation.

Think of it as: “Give me every key in this object.”

Syntax

for (const key in someObject) {
  // use key and someObject[key]
}
  • key → each property name (string)

  • someObject[key] → the value for that key

Basic example (objects)

const user = { name: "Gagan", role: "Developer", active: true };

for (const key in user) {
  console.log(key, "=>", user[key]);
}
// name => Gagan
// role => Developer
// active => true

Important behaviors to know

  1. Works on objects (not ideal for arrays): It loops keys, not values. For arrays, use for...of or .forEach().

  2. Includes inherited keys: By default, it also iterates keys that come from the object’s prototype chain.

  3. Ignores Symbol keys: Only string keys are visited.

  4. Enumeration order: Don’t rely on the order. (Integer-like keys tend to come first, but avoid depending on it.)

Filtering out inherited keys (best practice)

Because for...in also sees keys from prototypes, you’ll often add a check:

const user = Object.create({ fromProto: "hello" });
user.name = "Gagan";
user.role = "Developer";

for (const key in user) {
  if (Object.prototype.hasOwnProperty.call(user, key)) {
    console.log(key, user[key]); // prints only "name" and "role"
  }
}

Shortcut in modern JS:

if (Object.hasOwn(user, key)) { ... }

When should you use for...in?

  • When you want to iterate over all enumerable keys of a plain object, especially when you don’t need an array of keys first.

  • When you are okay handling (or filtering) inherited keys.

When should you avoid it?

  • Arrays: It loops indices as strings and can include extra/custom properties—use for...of, classic for, or .forEach().

  • When you need values directlyfor...of on Object.values(obj) or Object.entries(obj) is cleaner.

  • When you want a guaranteed order—convert to arrays with Object.keys/values/entries.

Examples you’ll actually use

1) Loop an object and read values

const product = { id: 101, title: "Keyboard", price: 799 };

for (const key in product) {
  if (Object.hasOwn(product, key)) {
    console.log(`${key}: ${product[key]}`);
  }
}

2) Build a new object while looping

const raw = { name: "  gagan  ", city: "  jaipur" };
const cleaned = {};

for (const key in raw) {
  if (Object.hasOwn(raw, key)) {
    cleaned[key] = String(raw[key]).trim();
  }
}
console.log(cleaned); // { name: "gagan", city: "jaipur" }

3) Count own properties

function countOwnProps(obj) {
  let count = 0;
  for (const key in obj) {
    if (Object.hasOwn(obj, key)) count++;
  }
  return count;
}

console.log(countOwnProps({ a: 1, b: 2, c: 3 })); // 3

4) Beware prototypes: inherited keys appear

const proto = { shared: true };
const obj = Object.create(proto);
obj.own = 123;

for (const key in obj) {
  console.log(key); // "own", then "shared" (inherited)
}

Filter with Object.hasOwn(obj, key) if you only want own.

5) Not recommended for arrays

const arr = ["apple", "banana"];
arr.extra = "x";

for (const i in arr) {
  console.log(i, arr[i]);
}
// "0" "apple"
// "1" "banana"
// "extra" "x"   <-- surprise!

Prefer:

for (const value of arr) console.log(value);
// apple
// banana

6) Compare: for...in vs for...of

const obj = { a: 10, b: 20 };
const arr = [10, 20];

// for...in  => keys (strings)
for (const k in obj) console.log(k);       // "a", "b"
for (const k in arr) console.log(k);       // "0", "1"

// for...of  => values (iterables only)
for (const v of arr) console.log(v);       // 10, 20
// for...of doesn't work directly on plain objects:
// for (const v of obj) {} // TypeError

// To use for...of with objects, convert with helpers:
for (const [k, v] of Object.entries(obj)) {
  console.log(k, v); // "a" 10, "b" 20
}

7) Skipping non-enumerable and Symbol keys

const o = {};
Object.defineProperty(o, "hidden", { value: 1, enumerable: false });
o.visible = 2;
const s = Symbol("id");
o[s] = 3;

for (const k in o) {
  console.log(k); // only "visible"
}

Handy patterns

Loop over keys (own only):

for (const key in obj) {
  if (!Object.hasOwn(obj, key)) continue;
  // use obj[key]
}

Safest, most readable alternative for many cases:

Object.entries(obj).forEach(([key, value]) => {
  // use key, value
});

Quick cheat sheet

  • Use for...in to iterate keys of an object.

  • Always guard with Object.hasOwn(obj, key) if you don’t want inherited keys.

  • Don’t use for...in for arrays; use for...of, .forEach(), or a classic for.

  • Symbol keys and non-enumerable properties are skipped.

  • Don’t rely on property order.

What is splice() in JavaScript

1. What is splice()?

The splice() method in JavaScript is used to change the contents of an array by:

  • Adding elements

  • Removing elements

  • Replacing elements

It modifies the original array (unlike slice() which returns a new array).

Here’s a visual diagram showing how splice() works step-by-step — starting from the original array, removing selected elements, and then inserting new elements at the chosen position.


2. Syntax

array.splice(start, deleteCount, item1, item2, ...);

Parameters

  1. start → The index where changes begin.

  2. deleteCount → Number of elements to remove.

  3. item1, item2, ... → (optional) Elements to add at the start index.


3. How it Works

Think of splice() as:

“Go to the position start, remove deleteCount items, and then insert any new items I give you at that same position.”


4. Examples

Example 1: Removing Elements

let fruits = ['apple', 'banana', 'orange', 'mango'];

// Remove 2 elements starting from index 1
let removed = fruits.splice(1, 2);

console.log(fruits); // ['apple', 'mango']
console.log(removed); // ['banana', 'orange']

Example 2: Adding Elements

let fruits = ['apple', 'mango'];

// Start at index 1, remove 0 elements, add 'banana' and 'orange'
fruits.splice(1, 0, 'banana', 'orange');

console.log(fruits); // ['apple', 'banana', 'orange', 'mango']

Example 3: Replacing Elements

let fruits = ['apple', 'banana', 'orange'];

// Start at index 1, remove 1 element, add 'mango'
fruits.splice(1, 1, 'mango');

console.log(fruits); // ['apple', 'mango', 'orange']

Example 4: Remove All from a Certain Index

let numbers = [1, 2, 3, 4, 5];

// Start at index 2, remove all remaining
numbers.splice(2);

console.log(numbers); // [1, 2]

5. Key Points

  • Modifies original array (in-place).

  • Returns an array of removed elements.

  • If deleteCount is 0 → no elements are removed (only adding happens).

  • If deleteCount is omitted → removes everything from start to end.

  • Works with any data type in an array.

What is reduce() in JavaScript

1. What is reduce()?

The reduce() method in JavaScript is used to “reduce” an array into a single value by running a function on each element of the array.

This single value could be:

  • A number (sum, product, average, etc.)

  • A string (concatenated sentence, joined names, etc.)

  • An object (grouped data, frequency count, etc.)

  • Even another array (flattening arrays, etc.)

Think of reduce() as:

"Take my array, go through it one item at a time, and keep combining the result until I have only one thing left."

Here’s a visual diagram showing how reduce() processes each step — starting from the initialValue and updating the accumulator after adding each array element. This makes it easier to see the flow from start to finish.


2. Syntax

array.reduce(callbackFunction, initialValue);

Parameters

  1. callbackFunction → The function that runs on each element.

    • It takes 4 arguments:

      function(accumulator, currentValue, currentIndex, array)
      
      • accumulator → The value that carries over between each loop.

      • currentValue → The current element being processed.

      • currentIndex → The index of the current element (optional).

      • array → The original array (optional).

  2. initialValue → (optional but highly recommended) The starting value for the accumulator.


3. How it works step-by-step

Let’s start with a simple example — summing numbers.

const numbers = [1, 2, 3, 4, 5];

const sum = numbers.reduce(function (accumulator, currentValue) {
  return accumulator + currentValue;
}, 0);

console.log(sum); // 15

Step-by-step execution:

  • Step 1: Initial accumulator = 0 (from initialValue)

  • Step 2: Add first number → 0 + 1 = 1

  • Step 3: Add second number → 1 + 2 = 3

  • Step 4: Add third number → 3 + 3 = 6

  • Step 5: Add fourth number → 6 + 4 = 10

  • Step 6: Add fifth number → 10 + 5 = 15

  • Final Result: 15


4. Using Arrow Function

const sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 15

Here:

  • acc = accumulator

  • curr = current value


5. More Examples

Example 1: Find Maximum Value

const numbers = [10, 25, 30, 5, 40];

const max = numbers.reduce((acc, curr) => {
  return curr > acc ? curr : acc;
}, numbers[0]);

console.log(max); // 40

Example 2: Count Occurrences

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

const count = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});

console.log(count);
// { apple: 3, banana: 2, orange: 1 }

Example 3: Flatten an Array

const nested = [[1, 2], [3, 4], [5, 6]];

const flat = nested.reduce((acc, curr) => acc.concat(curr), []);

console.log(flat); // [1, 2, 3, 4, 5, 6]

Example 4: Sum of Object Values

const items = [
  { name: 'Book', price: 200 },
  { name: 'Pen', price: 50 },
  { name: 'Bag', price: 500 }
];

const total = items.reduce((acc, item) => acc + item.price, 0);

console.log(total); // 750

6. Common Mistakes Beginners Make

❌ Forgetting to set the initialValue (can cause unexpected results if the array is empty).
❌ Confusing accumulator with currentValue.
❌ Thinking reduce() only works for numbers (it works for any data type!).


7. Why Use reduce()?

  • It’s powerful: Can replace many for or forEach loops.

  • It’s flexible: Works with numbers, strings, objects, arrays.

  • It’s cleaner: Keeps logic in one place.

Mail Proxy

1. Simple Meaning

A Mail Proxy in Nginx is like a middleman for email traffic — similar to how a reverse proxy works for websites, but here it’s for email protocols like:

  • SMTP (sending mail)

  • IMAP (reading mail)

  • POP3 (downloading mail)

Instead of connecting directly to the mail server, your email client connects to Nginx Mail Proxy, which then forwards the connection to the correct mail server.




2. Real-Life Example

Imagine:

  • You have two mail servers — one for Gmail, one for Outlook.

  • You want to provide one single address to users: mail.example.com.

  • Users connect here, and Nginx decides which actual server to send them to.

Like a post office front desk:

  • Customer hands you a letter (email connection).

  • You check where it needs to go.

  • You forward it to the right delivery office (mail server).


3. Why Use Mail Proxy?

  • Single Entry Point → Users don’t need to remember different server addresses.

  • Security → Hide actual mail server IPs from public.

  • SSL/TLS Offloading → Nginx handles encryption before passing to backend.

  • Load Balancing for Mail → Distribute connections among multiple mail servers.

  • Protocol Handling → Can support IMAP, POP3, SMTP in one place.


4. How it Works in Nginx

Nginx listens for email client connections on ports like:

  • 25, 465, 587 → SMTP

  • 110, 995 → POP3

  • 143, 993 → IMAP

When an email client connects:

  1. Nginx authenticates the user (via a backend or script).

  2. Based on authentication, Nginx selects the right mail server.

  3. Nginx forwards traffic between the client and the mail server.


5. Example Nginx Mail Proxy Config

mail {
    # Enable mail proxy for POP3, IMAP, SMTP
    server {
        listen 143;        # IMAP
        protocol imap;
        proxy_pass_error_message on;
        proxy on;
        starttls on;
        ssl_certificate /etc/ssl/cert.pem;
        ssl_certificate_key /etc/ssl/key.pem;
        auth_http 127.0.0.1:9000/auth;
    }

    server {
        listen 25;         # SMTP
        protocol smtp;
        proxy on;
        starttls on;
        ssl_certificate /etc/ssl/cert.pem;
        ssl_certificate_key /etc/ssl/key.pem;
        auth_http 127.0.0.1:9000/auth;
    }
}

Key parts:

  • protocol imap/smtp/pop3 → Defines which protocol the block handles.

  • starttls on; → Allows upgrading from plain to encrypted connection.

  • ssl_certificate → Handles SSL encryption.

  • auth_http → Calls an HTTP backend to authenticate users and tell Nginx which server to use.


6. Where It’s Used in Real Life

  • Large companies with multiple mail clusters behind one public address.

  • ISPs offering email hosting for many domains.

  • Mail services that hide backend changes (you can move mail servers without changing client settings).


7. Advantages

  • Centralized security & SSL

  • Easier scaling

  • Easier migration between mail servers

  • Unified configuration for multiple domains


HTTP Cache

1. Simple Meaning

HTTP Cache means storing a copy of the server’s response so that the next time someone requests the same thing, it can be served faster without asking the backend again.

Think of it like:

  • You go to a shop and ask for a Coke.

  • The shopkeeper gets it from the warehouse (backend server) — takes 5 minutes.

  • Next time, he already has Coke in the fridge (cache) — gives it in 5 seconds.




2. Why Caching is Important

Without caching:

  • Every request hits your backend.

  • Backend works harder, even for the same repeated request.

  • Slow responses under high traffic.

With caching:

  • Common requests are served from stored copies.

  • Backend is less busy.

  • Responses are much faster.


3. Types of Caching in HTTP

  1. Browser Cache → Stored in the user’s browser.

  2. Proxy Cache → Stored in a middle server like Nginx.

  3. CDN Cache → Stored in geographically distributed servers.

We’re focusing on Proxy Cache in Nginx.


4. How Nginx HTTP Cache Works

  1. First request → Nginx checks if response is cached.

  2. If not cached → Nginx asks backend → stores response in cache → sends to client.

  3. If cached → Nginx sends stored copy directly to client.


5. Nginx HTTP Cache Basic Setup

Let’s say you want to cache image files for 1 hour.

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m inactive=60m;

server {
    listen 80;
    server_name example.com;

    location /images/ {
        proxy_cache my_cache;
        proxy_cache_valid 200 1h;
        proxy_pass http://localhost:3000;
    }
}

Explanation:

  • /var/cache/nginx → Folder where cached files are stored.

  • keys_zone=my_cache:10m → Name + memory used for cache metadata.

  • inactive=60m → If not used for 60 minutes, remove from cache.

  • proxy_cache_valid 200 1h → Cache only successful responses (200 OK) for 1 hour.


6. Example Use Case

Without Cache:

  • 1000 users request the same /images/logo.png

  • Backend gets 1000 hits.

With Cache:

  • First request → Backend

  • Next 999 requests → Served from Nginx cache (0 backend hits).


7. Advanced Cache Controls

  • Cache different URLs differently:

location /api/ {
    proxy_cache my_cache;
    proxy_cache_valid 200 10m;
}
location /images/ {
    proxy_cache my_cache;
    proxy_cache_valid 200 1h;
}
  • Bypass cache for logged-in users:

location / {
    if ($http_cookie ~* "session_id") {
        proxy_no_cache 1;
        proxy_cache_bypass 1;
    }
}
  • Clear cache manually (delete files in /var/cache/nginx).


8. Benefits of HTTP Cache

  • Faster load time

  • Less load on backend

  • Better scalability

  • Lower bandwidth cost


What is slice() in JavaScript

What is slice() ? slice() is a method used to copy a portion of an array or string without changing the original . Think of it like cut...