JavaScript Tutorial

JavaScript String Operators: Complete Guide with Examples

Master JavaScript string operators including concatenation (+, +=), template literals, string comparison, and type coercion. Learn string manipulation with practical examples and best practices.

Welcome back! 👋 In the previous lessons, you learned about arithmetic, assignment, comparison, and logical operators. Now, let's explore string operators - operators specifically designed for working with text data.

Strings are one of the most commonly used data types in JavaScript. Understanding how to manipulate, combine, and compare strings is essential for building real-world applications!


What are String Operators?

String operators are used to manipulate and work with string (text) data. While JavaScript doesn't have as many dedicated string operators as it does for numbers, the ones it has are powerful and frequently used.

The main string operators are:

  1. Concatenation operator (+) - Joins strings together
  2. Concatenation assignment (+=) - Appends to existing string
  3. Template literals (${}) - Modern string interpolation
  4. Comparison operators - Compare strings lexicographically
// Concatenation
let greeting = "Hello" + " " + "World";
console.log(greeting); // "Hello World"

// Template literals
let name = "Mihir";
let message = `Welcome, ${name}!`;
console.log(message); // "Welcome, Mihir!"

// Comparison
console.log("apple" < "banana"); // true

String Concatenation (+)

The addition operator (+) is used to join (concatenate) two or more strings together.

Basic Concatenation

// Joining two strings
let firstName = "John";
let lastName = "Doe";
let fullName = firstName + " " + lastName;
console.log(fullName); // "John Doe"

// Joining multiple strings
let greeting = "Hello" + " " + "there" + "!";
console.log(greeting); // "Hello there!"

// With variables and literals
let city = "New York";
let message = "I live in " + city + ".";
console.log(message); // "I live in New York."

Concatenation with Numbers

Important: When you use + with a string and a number, JavaScript converts the number to a string and concatenates them.

// String + Number = String (concatenation)
console.log("The answer is " + 42);        // "The answer is 42"
console.log("Value: " + 100);              // "Value: 100"
console.log("Score: " + 85.5);             // "Score: 85.5"

// Number + String = String (concatenation)
console.log(10 + " items");                // "10 items"
console.log(5 + " stars");                 // "5 stars"

// Order matters!
console.log(5 + 3 + " total");             // "8 total" (5+3=8, then "8"+"total")
console.log("Total: " + 5 + 3);            // "Total: 53" ("Total: 5"+"3")
console.log("Total: " + (5 + 3));          // "Total: 8" (parentheses force addition first)

Type Coercion with Concatenation

// Different types get converted to strings
console.log("Value: " + true);             // "Value: true"
console.log("Result: " + false);           // "Result: false"
console.log("Data: " + null);              // "Data: null"
console.log("Value: " + undefined);        // "Value: undefined"

// Arrays convert to comma-separated strings
console.log("Numbers: " + [1, 2, 3]);      // "Numbers: 1,2,3"

// Objects convert to [object Object]
console.log("Object: " + {a: 1});          // "Object: [object Object]"

Multi-Line Concatenation

// Breaking long strings across lines
let longMessage = "This is a very long message " +
                  "that spans multiple lines " +
                  "to make the code more readable.";

console.log(longMessage);

// Building HTML
let html = "<div>" +
           "  <h1>Title</h1>" +
           "  <p>Paragraph text</p>" +
           "</div>";

console.log(html);

Practical Examples

// Creating a greeting
let firstName = "Alice";
let lastName = "Johnson";
let fullGreeting = "Hello, " + firstName + " " + lastName + "!";
console.log(fullGreeting); // "Hello, Alice Johnson!"

// Building a URL
let baseUrl = "https://api.example.com";
let endpoint = "/users";
let userId = 123;
let apiUrl = baseUrl + endpoint + "/" + userId;
console.log(apiUrl); // "https://api.example.com/users/123"

// Creating formatted output
let product = "Laptop";
let price = 999.99;
let quantity = 2;
let receipt = "Product: " + product + "\n" +
              "Price: $" + price + "\n" +
              "Quantity: " + quantity + "\n" +
              "Total: $" + (price * quantity);
console.log(receipt);
// Product: Laptop
// Price: $999.99
// Quantity: 2
// Total: $1999.98

// Email template
let userName = "John";
let subject = "Welcome";
let email = "Dear " + userName + ",\n\n" +
            "Thank you for signing up!\n\n" +
            "Best regards,\n" +
            "The Team";
console.log(email);

String Concatenation Assignment (+=)

The concatenation assignment operator (+=) appends a string to an existing string variable.

Basic Usage

let message = "Hello";
message += " World";
console.log(message); // "Hello World"

// Equivalent to:
message = message + " World";

Building Strings Incrementally

// Building a message piece by piece
let story = "Once upon a time";
story += ", there was a developer";
story += " who loved JavaScript.";
story += " The end.";
console.log(story);
// "Once upon a time, there was a developer who loved JavaScript. The end."

// Building a list
let shoppingList = "Shopping List:\n";
shoppingList += "- Milk\n";
shoppingList += "- Bread\n";
shoppingList += "- Eggs\n";
shoppingList += "- Butter\n";
console.log(shoppingList);

In Loops

// Concatenating in a loop
let numbers = "";
for (let i = 1; i <= 5; i++) {
  numbers += i;
  if (i < 5) {
    numbers += ", ";
  }
}
console.log(numbers); // "1, 2, 3, 4, 5"

// Building HTML list
let items = ["Apple", "Banana", "Orange"];
let htmlList = "<ul>\n";
for (let item of items) {
  htmlList += "  <li>" + item + "</li>\n";
}
htmlList += "</ul>";
console.log(htmlList);
// <ul>
//   <li>Apple</li>
//   <li>Banana</li>
//   <li>Orange</li>
// </ul>

Practical Examples

// Building a CSV string
function arrayToCSV(data) {
  let csv = "";
  for (let i = 0; i < data.length; i++) {
    csv += data[i];
    if (i < data.length - 1) {
      csv += ",";
    }
  }
  return csv;
}

console.log(arrayToCSV([1, 2, 3, 4, 5])); // "1,2,3,4,5"

// Log accumulator
let log = "";

function addLog(message) {
  let timestamp = new Date().toLocaleTimeString();
  log += "[" + timestamp + "] " + message + "\n";
}

addLog("Application started");
addLog("User logged in");
addLog("Data loaded");
console.log(log);

// Query string builder
let params = "";
params += "name=" + encodeURIComponent("John Doe");
params += "&";
params += "age=" + 30;
params += "&";
params += "city=" + encodeURIComponent("New York");

let url = "https://api.example.com/search?" + params;
console.log(url);
// https://api.example.com/search?name=John%20Doe&age=30&city=New%20York

Template Literals (Modern String Interpolation)

Template literals (introduced in ES6) provide a more elegant and readable way to work with strings, especially when embedding expressions.

Basic Syntax

Template literals use backticks (`) instead of quotes:

// Old way
let name = "John";
let greeting = "Hello, " + name + "!";

// Modern way with template literals
let greeting2 = `Hello, ${name}!`;

console.log(greeting);  // "Hello, John!"
console.log(greeting2); // "Hello, John!"

String Interpolation with ${}

You can embed any JavaScript expression inside ${}:

// Variables
let firstName = "John";
let lastName = "Doe";
let fullName = `${firstName} ${lastName}`;
console.log(fullName); // "John Doe"

// Expressions
let a = 5;
let b = 10;
console.log(`Sum: ${a + b}`);              // "Sum: 15"
console.log(`Product: ${a * b}`);          // "Product: 50"

// Function calls
function getDiscount() {
  return 20;
}
console.log(`Discount: ${getDiscount()}%`); // "Discount: 20%"

// Ternary operators
let age = 25;
console.log(`You are ${age >= 18 ? "an adult" : "a minor"}`);
// "You are an adult"

// Object properties
let user = { name: "Alice", role: "Admin" };
console.log(`User: ${user.name}, Role: ${user.role}`);
// "User: Alice, Role: Admin"

// Array elements
let colors = ["red", "green", "blue"];
console.log(`First color: ${colors[0]}`); // "First color: red"

Multi-Line Strings

Template literals preserve line breaks and whitespace:

// Old way (using \n)
let oldMessage = "Line 1\nLine 2\nLine 3";

// New way (natural line breaks)
let newMessage = `Line 1
Line 2
Line 3`;

console.log(newMessage);
// Line 1
// Line 2
// Line 3

// HTML template
let html = `
  <div class="card">
    <h2>Title</h2>
    <p>This is a paragraph.</p>
  </div>
`;

console.log(html);

// Email template
let email = `
Dear ${userName},

Thank you for your order #${orderId}.

Your items will be delivered within ${deliveryDays} business days.

Best regards,
The Team
`;

Nested Template Literals

You can nest template literals inside each other:

let items = ["Apple", "Banana", "Orange"];

let list = `
  <ul>
    ${items.map(item => `<li>${item}</li>`).join('\n    ')}
  </ul>
`;

console.log(list);
// <ul>
//   <li>Apple</li>
//   <li>Banana</li>
//   <li>Orange</li>
// </ul>

Practical Examples

// Invoice template
let invoiceNumber = 12345;
let customerName = "John Doe";
let itemName = "Laptop";
let price = 999.99;
let quantity = 2;
let tax = 0.08;

let subtotal = price * quantity;
let taxAmount = subtotal * tax;
let total = subtotal + taxAmount;

let invoice = `
═══════════════════════════════════
           INVOICE #${invoiceNumber}
═══════════════════════════════════

Customer: ${customerName}

Item:     ${itemName}
Price:    $${price.toFixed(2)}
Quantity: ${quantity}
───────────────────────────────────
Subtotal: $${subtotal.toFixed(2)}
Tax:      $${taxAmount.toFixed(2)}
───────────────────────────────────
TOTAL:    $${total.toFixed(2)}
═══════════════════════════════════
`;

console.log(invoice);

// API request URL
let baseUrl = "https://api.example.com";
let resource = "users";
let id = 123;
let queryParams = { active: true, limit: 10 };

let apiUrl = `${baseUrl}/${resource}/${id}?active=${queryParams.active}&limit=${queryParams.limit}`;
console.log(apiUrl);
// https://api.example.com/users/123?active=true&limit=10

// User card
let user = {
  name: "Alice Johnson",
  email: "alice@example.com",
  role: "Administrator",
  joinDate: "2023-01-15"
};

let userCard = `
╔═══════════════════════════════╗
║ USER PROFILE                  ║
╠═══════════════════════════════╣
║ Name:  ${user.name.padEnd(23)}║
║ Email: ${user.email.padEnd(23)}║
║ Role:  ${user.role.padEnd(23)}║
║ Since: ${user.joinDate.padEnd(23)}║
╚═══════════════════════════════╝
`;

console.log(userCard);

// Conditional messages
function getGreeting(hour) {
  return `Good ${hour < 12 ? 'morning' : hour < 18 ? 'afternoon' : 'evening'}!`;
}

console.log(getGreeting(9));   // "Good morning!"
console.log(getGreeting(14));  // "Good afternoon!"
console.log(getGreeting(20));  // "Good evening!"

// Status badge
function getStatusBadge(status) {
  let emoji = status === 'active' ? '✓' : status === 'pending' ? '⏳' : '✗';
  let color = status === 'active' ? 'green' : status === 'pending' ? 'yellow' : 'red';
  
  return `[${emoji}] Status: ${status.toUpperCase()} (${color})`;
}

console.log(getStatusBadge('active'));   // "[✓] Status: ACTIVE (green)"
console.log(getStatusBadge('pending'));  // "[⏳] Status: PENDING (yellow)"
console.log(getStatusBadge('inactive')); // "[✗] Status: INACTIVE (red)"

Tagged Template Literals (Advanced)

Tagged templates allow you to process template literals with a function.

Basic Tagged Template

// Tag function
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => {
    return result + str + (values[i] ? `<mark>${values[i]}</mark>` : '');
  }, '');
}

let name = "John";
let age = 30;
let message = highlight`User ${name} is ${age} years old.`;

console.log(message);
// "User <mark>John</mark> is <mark>30</mark> years old."

Practical Examples

// SQL query builder (safe from SQL injection)
function sql(strings, ...values) {
  return {
    text: strings.join('?'),
    values: values
  };
}

let userId = 123;
let query = sql`SELECT * FROM users WHERE id = ${userId}`;
console.log(query);
// { text: "SELECT * FROM users WHERE id = ?", values: [123] }

// Currency formatter
function currency(strings, ...values) {
  return strings.reduce((result, str, i) => {
    let value = values[i];
    let formatted = value !== undefined 
      ? '$' + value.toFixed(2) 
      : '';
    return result + str + formatted;
  }, '');
}

let price = 19.99;
let total = 99.99;
let receipt = currency`Price: ${price}, Total: ${total}`;
console.log(receipt); // "Price: $19.99, Total: $99.99"

// HTML sanitizer
function sanitize(strings, ...values) {
  function escape(str) {
    return String(str)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#039;');
  }
  
  return strings.reduce((result, str, i) => {
    return result + str + (values[i] ? escape(values[i]) : '');
  }, '');
}

let userInput = '<script>alert("XSS")</script>';
let safe = sanitize`User entered: ${userInput}`;
console.log(safe);
// "User entered: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;"

String Comparison

Strings can be compared using comparison operators. JavaScript compares strings lexicographically (dictionary order) using Unicode values.

Basic String Comparison

// Equality
console.log("hello" === "hello");   // true
console.log("hello" === "Hello");   // false (case-sensitive)
console.log("abc" === "abc");       // true

// Inequality
console.log("hello" !== "world");   // true
console.log("hello" !== "hello");   // false

Lexicographical Comparison

Strings are compared character by character from left to right:

// Alphabetical order
console.log("a" < "b");             // true
console.log("apple" < "banana");    // true (a < b)
console.log("cat" < "dog");         // true (c < d)

// Character-by-character comparison
console.log("abc" < "abd");         // true (c < d)
console.log("apple" < "application"); // true (shorter comes first when prefix matches)

// Greater than
console.log("zebra" > "apple");     // true (z > a)
console.log("world" > "hello");     // true (w > h)

Case Sensitivity in Comparison

Important: Uppercase letters have lower Unicode values than lowercase letters!

// Uppercase vs lowercase
console.log("A" < "a");             // true (A=65, a=97)
console.log("Z" < "a");             // true (Z=90, a=97)
console.log("Apple" < "banana");    // true (A < b)

// All uppercase comes before lowercase
console.log("ABC" < "abc");         // true
console.log("ZEBRA" < "apple");     // true

// Case-insensitive comparison (convert to same case)
let str1 = "Apple";
let str2 = "apple";
console.log(str1.toLowerCase() === str2.toLowerCase()); // true

Number Strings (Lexicographical!)

// ⚠️ String comparison, NOT numeric!
console.log("10" < "9");            // true! ("1" < "9")
console.log("2" > "10");            // true! ("2" > "1")
console.log("100" < "20");          // true! ("1" < "2")

// For numeric comparison, convert to numbers
console.log(Number("10") < Number("9"));    // false (10 < 9)
console.log(parseInt("10") < parseInt("9")); // false
console.log("10" - 0 < "9" - 0);             // false

Empty String Comparison

// Empty string is "smallest"
console.log("" < "a");              // true
console.log("" < "0");              // true
console.log("" === "");             // true

// Space is not empty
console.log(" " === "");            // false
console.log(" " > "");              // true

Practical Examples

// Alphabetical sorting
let names = ["Charlie", "Alice", "Bob", "David"];
names.sort();
console.log(names); // ["Alice", "Bob", "Charlie", "David"]

// Case-insensitive sorting
let names2 = ["charlie", "Alice", "bob", "David"];
names2.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
console.log(names2); // ["Alice", "bob", "charlie", "David"]

// Checking prefix
function startsWith(str, prefix) {
  return str.slice(0, prefix.length) === prefix;
}

console.log(startsWith("hello world", "hello")); // true
console.log(startsWith("hello world", "world")); // false

// Version comparison (simple)
function compareVersions(v1, v2) {
  let parts1 = v1.split('.').map(Number);
  let parts2 = v2.split('.').map(Number);
  
  for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
    let num1 = parts1[i] || 0;
    let num2 = parts2[i] || 0;
    
    if (num1 < num2) return -1;
    if (num1 > num2) return 1;
  }
  
  return 0;
}

console.log(compareVersions("1.2.3", "1.2.4")); // -1 (first is older)
console.log(compareVersions("2.0.0", "1.9.9")); // 1 (first is newer)
console.log(compareVersions("1.0.0", "1.0.0")); // 0 (equal)

// Password validation
function isStrongPassword(password) {
  return password.length >= 8 &&
         /[A-Z]/.test(password) &&  // Has uppercase
         /[a-z]/.test(password) &&  // Has lowercase
         /[0-9]/.test(password);    // Has number
}

console.log(isStrongPassword("Pass123"));   // false (too short)
console.log(isStrongPassword("Password123")); // true

String Methods as Operators

While not technically operators, string methods are commonly used for string manipulation:

Common String Methods

let text = "Hello World";

// Length
console.log(text.length);               // 11

// Case conversion
console.log(text.toLowerCase());        // "hello world"
console.log(text.toUpperCase());        // "HELLO WORLD"

// Substring extraction
console.log(text.slice(0, 5));          // "Hello"
console.log(text.substring(6, 11));     // "World"
console.log(text.substr(6, 5));         // "World" (deprecated)

// Searching
console.log(text.indexOf("World"));     // 6
console.log(text.lastIndexOf("l"));     // 9
console.log(text.includes("World"));    // true
console.log(text.startsWith("Hello"));  // true
console.log(text.endsWith("World"));    // true

// Replacing
console.log(text.replace("World", "JavaScript")); // "Hello JavaScript"
console.log(text.replaceAll("l", "L"));          // "HeLLo WorLd"

// Splitting
console.log(text.split(" "));           // ["Hello", "World"]
console.log(text.split(""));            // ["H", "e", "l", "l", "o", ...]

// Trimming
let padded = "  hello  ";
console.log(padded.trim());             // "hello"
console.log(padded.trimStart());        // "hello  "
console.log(padded.trimEnd());          // "  hello"

// Padding
console.log("5".padStart(3, "0"));      // "005"
console.log("5".padEnd(3, "0"));        // "500"

// Repeating
console.log("Ha".repeat(3));            // "HaHaHa"

Chaining Methods

let message = "  Hello World  ";

let formatted = message
  .trim()
  .toLowerCase()
  .replace("world", "javascript")
  .split(" ")
  .join("-");

console.log(formatted); // "hello-javascript"

// Building a slug
function createSlug(text) {
  return text
    .toLowerCase()
    .trim()
    .replace(/[^\w\s-]/g, '')  // Remove special chars
    .replace(/\s+/g, '-')       // Replace spaces with hyphens
    .replace(/-+/g, '-');       // Replace multiple hyphens with single
}

console.log(createSlug("Hello World! This is a TEST.")); 
// "hello-world-this-is-a-test"

Performance Considerations

String Concatenation Performance

// ❌ Slow for many iterations (creates new string each time)
let result = "";
for (let i = 0; i < 10000; i++) {
  result += "x";  // Creates 10,000 new strings
}

// ✅ Faster - use array join for many concatenations
let parts = [];
for (let i = 0; i < 10000; i++) {
  parts.push("x");
}
let result2 = parts.join("");  // Only one string creation

// ✅ Modern engines optimize template literals well
let result3 = "";
for (let i = 0; i < 10000; i++) {
  result3 += `x`;
}

When to Use Each Method

// Simple concatenation: Use any method
let name = "John";
let greeting1 = "Hello, " + name;           // ✅ Simple and clear
let greeting2 = `Hello, ${name}`;           // ✅ Modern and clean

// Multiple variables: Template literals are cleaner
let first = "John";
let last = "Doe";
let age = 30;

// ❌ Hard to read
let info1 = "Name: " + first + " " + last + ", Age: " + age;

// ✅ Much cleaner
let info2 = `Name: ${first} ${last}, Age: ${age}`;

// Building in loops: Use array join
let items = ["a", "b", "c"];

// ❌ Less efficient
let str1 = "";
for (let item of items) {
  str1 += item + ",";
}

// ✅ More efficient
let str2 = items.join(",");

// Multi-line strings: Template literals
// ❌ Hard to read
let html1 = "<div>\n" +
            "  <h1>Title</h1>\n" +
            "  <p>Text</p>\n" +
            "</div>";

// ✅ Natural
let html2 = `
<div>
  <h1>Title</h1>
  <p>Text</p>
</div>
`;

Common Mistakes and Pitfalls

Mistake 1: String vs Number Addition

// ❌ Problem - string concatenation instead of addition
let a = "10";
let b = "20";
let sum = a + b;
console.log(sum);  // "1020" (not 30!)

// ✅ Solution - convert to numbers first
let sum2 = Number(a) + Number(b);
console.log(sum2); // 30

let sum3 = parseInt(a) + parseInt(b);
console.log(sum3); // 30

Mistake 2: Order of Operations

// ❌ Unexpected result
console.log(5 + 3 + " is the sum");     // "8 is the sum" ✅
console.log("The sum is " + 5 + 3);     // "The sum is 53" ❌

// ✅ Use parentheses
console.log("The sum is " + (5 + 3));   // "The sum is 8" ✅

// ✅ Or use template literals
console.log(`The sum is ${5 + 3}`);     // "The sum is 8" ✅

Mistake 3: Comparing Number Strings

// ❌ Lexicographical comparison
console.log("10" > "9");    // false (string comparison!)
console.log("100" < "20");  // true (string comparison!)

// ✅ Convert to numbers
console.log(Number("10") > Number("9"));    // true
console.log(parseInt("100") < parseInt("20")); // false

Mistake 4: Case-Sensitive Comparison

// ❌ Case matters
console.log("Hello" === "hello");  // false

// ✅ Case-insensitive comparison
console.log("Hello".toLowerCase() === "hello".toLowerCase()); // true

// ✅ Or use locale compare
console.log("Hello".localeCompare("hello", undefined, { sensitivity: 'base' }) === 0); // true

Mistake 5: Modifying Strings (They're Immutable!)

// ❌ Strings are immutable
let str = "hello";
str[0] = "H";
console.log(str);  // Still "hello" (didn't change!)

// ✅ Create new string
let str2 = "hello";
str2 = "H" + str2.slice(1);
console.log(str2); // "Hello"

// ✅ Or use replace
let str3 = "hello";
str3 = str3.replace("h", "H");
console.log(str3); // "Hello"

Mistake 6: Empty String vs Null/Undefined

// ❌ Treating empty string as no value
let name = "";
if (!name) {
  console.log("No name");  // Executes, but "" might be valid
}

// ✅ Check specifically for empty string
if (name === "") {
  console.log("Empty string");
}

// ✅ Check for null/undefined
if (name == null) {
  console.log("No value");
}

Best Practices

1. Use Template Literals for Readability

// ❌ Hard to read
let message = "User " + user.name + " (ID: " + user.id + ") has " + user.points + " points";

// ✅ Much clearer
let message2 = `User ${user.name} (ID: ${user.id}) has ${user.points} points`;

2. Be Explicit with Type Conversions

// ❌ Relying on coercion
let total = price + "";  // Converts to string

// ✅ Explicit
let total2 = String(price);
let total3 = price.toString();

3. Use Array Join for Multiple Concatenations

// ❌ Inefficient in loops
let csv = "";
for (let value of values) {
  csv += value + ",";
}

// ✅ Efficient
let csv2 = values.join(",");

4. Trim User Input

// ❌ Not trimming input
function validateUsername(username) {
  return username.length >= 3;
}

// ✅ Trim whitespace
function validateUsername2(username) {
  return username.trim().length >= 3;
}

5. Use Appropriate String Methods

// ❌ Manual checking
if (str.slice(0, 5) === "Hello") { }

// ✅ Use built-in method
if (str.startsWith("Hello")) { }

// ❌ Manual case conversion
if (str.toLowerCase() === "hello") { }

// ✅ Use locale compare for case-insensitive
if (str.localeCompare("hello", undefined, { sensitivity: 'base' }) === 0) { }

6. Sanitize User Input

// ❌ Dangerous - potential XSS
let userInput = '<script>alert("XSS")</script>';
let html = `<div>${userInput}</div>`;

// ✅ Escape HTML
function escapeHTML(str) {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');
}

let safeHtml = `<div>${escapeHTML(userInput)}</div>`;

Practical Examples

Example 1: URL Builder

class URLBuilder {
  constructor(base) {
    this.base = base;
    this.path = "";
    this.params = [];
  }
  
  addPath(segment) {
    this.path += `/${segment}`;
    return this;
  }
  
  addParam(key, value) {
    this.params.push(`${key}=${encodeURIComponent(value)}`);
    return this;
  }
  
  build() {
    let url = this.base + this.path;
    if (this.params.length > 0) {
      url += "?" + this.params.join("&");
    }
    return url;
  }
}

let url = new URLBuilder("https://api.example.com")
  .addPath("users")
  .addPath("123")
  .addPath("posts")
  .addParam("limit", 10)
  .addParam("sort", "date")
  .addParam("filter", "published")
  .build();

console.log(url);
// https://api.example.com/users/123/posts?limit=10&sort=date&filter=published

Example 2: Template Engine

function renderTemplate(template, data) {
  return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
    return data[key] !== undefined ? data[key] : match;
  });
}

let template = "Hello {{name}}, you have {{count}} new messages.";
let data = { name: "John", count: 5 };
let result = renderTemplate(template, data);

console.log(result);
// "Hello John, you have 5 new messages."

// More complex template
let emailTemplate = `
Dear {{firstName}} {{lastName}},

Your order #{{orderId}} has been shipped!

Items:
{{items}}

Total: ${{total}}

Thank you for shopping with us!
`;

let orderData = {
  firstName: "Jane",
  lastName: "Doe",
  orderId: "12345",
  items: "- Laptop\n- Mouse\n- Keyboard",
  total: "1299.99"
};

console.log(renderTemplate(emailTemplate, orderData));

Example 3: Text Formatter

class TextFormatter {
  constructor(text) {
    this.text = text;
  }
  
  capitalize() {
    this.text = this.text.charAt(0).toUpperCase() + this.text.slice(1);
    return this;
  }
  
  titleCase() {
    this.text = this.text
      .toLowerCase()
      .split(' ')
      .map(word => word.charAt(0).toUpperCase() + word.slice(1))
      .join(' ');
    return this;
  }
  
  truncate(length, suffix = '...') {
    if (this.text.length > length) {
      this.text = this.text.slice(0, length - suffix.length) + suffix;
    }
    return this;
  }
  
  removeExtraSpaces() {
    this.text = this.text.trim().replace(/\s+/g, ' ');
    return this;
  }
  
  toString() {
    return this.text;
  }
}

let formatted = new TextFormatter("  hello   world  ")
  .removeExtraSpaces()
  .titleCase()
  .toString();

console.log(formatted); // "Hello World"

let truncated = new TextFormatter("This is a very long text that needs to be shortened")
  .truncate(20)
  .toString();

console.log(truncated); // "This is a very lo..."

Example 4: String Validator

class StringValidator {
  constructor(value) {
    this.value = value;
    this.errors = [];
  }
  
  required(message = "This field is required") {
    if (!this.value || this.value.trim() === "") {
      this.errors.push(message);
    }
    return this;
  }
  
  minLength(length, message) {
    if (this.value.length < length) {
      this.errors.push(message || `Minimum length is ${length} characters`);
    }
    return this;
  }
  
  maxLength(length, message) {
    if (this.value.length > length) {
      this.errors.push(message || `Maximum length is ${length} characters`);
    }
    return this;
  }
  
  pattern(regex, message) {
    if (!regex.test(this.value)) {
      this.errors.push(message || "Invalid format");
    }
    return this;
  }
  
  email(message = "Invalid email address") {
    let emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return this.pattern(emailRegex, message);
  }
  
  isValid() {
    return this.errors.length === 0;
  }
  
  getErrors() {
    return this.errors;
  }
}

// Validate email
let emailValidator = new StringValidator("user@example.com")
  .required()
  .email();

console.log(emailValidator.isValid()); // true

// Validate username
let usernameValidator = new StringValidator("ab")
  .required()
  .minLength(3, "Username must be at least 3 characters")
  .maxLength(20, "Username must be less than 20 characters")
  .pattern(/^[a-zA-Z0-9_]+$/, "Username can only contain letters, numbers, and underscores");

console.log(usernameValidator.isValid()); // false
console.log(usernameValidator.getErrors()); 
// ["Username must be at least 3 characters"]

Practice Exercises

Create a file called string-practice.js:

// String Operators Practice

// Exercise 1: Full Name Builder
function buildFullName(firstName, middleName, lastName) {
  // TODO: Build full name with proper spacing
  // Handle missing middle name (use empty string if null/undefined)
  // Example: buildFullName("John", "Michael", "Doe") → "John Michael Doe"
  // Example: buildFullName("Jane", null, "Smith") → "Jane Smith"
}

// Test
console.log(buildFullName("John", "Michael", "Doe"));
console.log(buildFullName("Jane", null, "Smith"));
console.log(buildFullName("Bob", "", "Johnson"));

// Exercise 2: Create Initials
function getInitials(fullName) {
  // TODO: Return initials from full name
  // Example: "John Michael Doe" → "J.M.D."
  // Handle extra spaces
}

// Test
console.log(getInitials("John Michael Doe"));      // "J.M.D."
console.log(getInitials("Jane Smith"));            // "J.S."
console.log(getInitials("  Bob   Johnson  "));    // "B.J."

// Exercise 3: Mask Email
function maskEmail(email) {
  // TODO: Mask email address
  // Show first 2 chars and last 2 chars before @, rest as ***
  // Example: "john.doe@example.com" → "jo***oe@example.com"
}

// Test
console.log(maskEmail("john.doe@example.com"));
console.log(maskEmail("alice@test.com"));

// Exercise 4: Build Query String
function buildQueryString(params) {
  // TODO: Convert object to URL query string
  // Example: { name: "John", age: 30 } → "name=John&age=30"
  // Use encodeURIComponent for values
}

// Test
console.log(buildQueryString({ name: "John Doe", age: 30, city: "New York" }));

// Exercise 5: Repeat Pattern
function createPattern(char, count, separator = "") {
  // TODO: Create repeated pattern
  // Example: createPattern("*", 5, "-") → "*-*-*-*-*"
  // Example: createPattern("Ha", 3) → "HaHaHa"
}

// Test
console.log(createPattern("*", 5, "-"));
console.log(createPattern("Ha", 3));
console.log(createPattern("=", 10));

// Exercise 6: Title Case
function toTitleCase(str) {
  // TODO: Convert string to title case
  // First letter of each word capitalized
  // Handle multiple spaces
  // Example: "hello world" → "Hello World"
}

// Test
console.log(toTitleCase("hello world"));
console.log(toTitleCase("the quick brown fox"));
console.log(toTitleCase("  multiple   spaces  "));

// Exercise 7: Create Slug
function createSlug(title) {
  // TODO: Convert title to URL-friendly slug
  // Lowercase, replace spaces with hyphens, remove special chars
  // Example: "Hello World!" → "hello-world"
  // Example: "JavaScript Tutorial #1" → "javascript-tutorial-1"
}

// Test
console.log(createSlug("Hello World!"));
console.log(createSlug("JavaScript Tutorial #1"));
console.log(createSlug("This is a TEST!!!"));

// Exercise 8: Count Occurrences
function countOccurrences(text, substring) {
  // TODO: Count how many times substring appears in text
  // Case-insensitive
  // Example: countOccurrences("Hello hello HELLO", "hello") → 3
}

// Test
console.log(countOccurrences("Hello hello HELLO", "hello"));
console.log(countOccurrences("JavaScript is awesome. I love JavaScript!", "JavaScript"));

Summary

Congratulations! 🎉 You now have a comprehensive understanding of string operators in JavaScript.

Key Takeaways

String Concatenation (+)

  • Joins strings together
  • Converts other types to strings
  • Order matters in mixed expressions

Concatenation Assignment (+=)

  • Appends to existing string
  • Useful for building strings incrementally
  • Common in loops

Template Literals (${})

  • Modern string interpolation
  • Multi-line strings
  • Embed expressions
  • More readable than concatenation

String Comparison

  • Lexicographical (dictionary) order
  • Case-sensitive by default
  • Number strings compared as strings, not numbers
  • Use locale methods for advanced comparison

Best Practices:

  • Use template literals for complex strings
  • Be explicit with type conversions
  • Use array join for many concatenations
  • Trim user input
  • Sanitize for security
  • Choose the right method for the task

Common Pitfalls:

  • String + Number = String (concatenation)
  • Order of operations matters
  • Strings are immutable
  • Number strings compare lexicographically
  • Case sensitivity in comparisons

What's Next?

Great job! You now understand how to work with strings in JavaScript.

In the next lesson, we'll explore Bitwise Operators where you'll learn:

  • Bitwise AND (&)
  • Bitwise OR (|)
  • Bitwise XOR (^)
  • Bitwise NOT (~)
  • Left/Right shift (<<, >>, >>>)
  • Practical use cases (flags, permissions, optimization)