Strings are one of the most fundamental data types in JavaScript, used to represent text data. JavaScript provides a rich set of methods and features for working with strings, from basic manipulation to advanced pattern matching and Unicode support.
In this tutorial, we'll explore string creation, manipulation methods, template literals, regular expressions, and best practices for working with string data in JavaScript.
String Basics
Understanding how strings work in JavaScript and the different ways to create them.
Creating Strings
// String literals
let singleQuote = 'Hello World';
let doubleQuote = "Hello World";
let mixed = 'It\'s a "beautiful" day';
// Template literals (ES6)
let template = `Hello World`;
let multiline = `This is a
multi-line
string`;
// String constructor
let fromConstructor = new String('Hello World');
console.log(typeof fromConstructor); // "object"
console.log(typeof singleQuote); // "string"
// Converting to string
let num = 42;
let str = String(num); // "42"
let str2 = num.toString(); // "42"
// Empty strings
let empty = '';
let alsoEmpty = "";
// String with special characters
let special = 'Line 1\nLine 2\tTabbed';
let unicode = 'Hello \u03A9 World'; // Hello © World
String Properties
// Length property
let text = 'Hello World';
console.log(text.length); // 11
// Accessing characters (modern)
console.log(text[0]); // 'H'
console.log(text[4]); // 'o'
// Accessing characters (traditional)
console.log(text.charAt(0)); // 'H'
console.log(text.charAt(10)); // 'd'
// Out of bounds access
console.log(text[20]); // undefined
console.log(text.charAt(20)); // '' (empty string)
// Unicode characters
let emoji = '👋 Hello';
console.log(emoji.length); // 7 (emoji counts as 2 code units)
console.log(emoji[0]); // '�' (part of emoji)
console.log(emoji.codePointAt(0)); // 128075 (wave emoji)
String Methods
JavaScript provides numerous built-in methods for string manipulation.
Case Conversion
// toUpperCase()
let text = 'Hello World';
console.log(text.toUpperCase()); // 'HELLO WORLD'
// toLowerCase()
console.log(text.toLowerCase()); // 'hello world'
// toLocaleUpperCase() - respects locale
console.log(text.toLocaleUpperCase('tr')); // Turkish locale
// toLocaleLowerCase() - respects locale
console.log(text.toLocaleLowerCase('tr')); // Turkish locale
// Practical usage
function searchCaseInsensitive(searchTerm, text) {
return text.toLowerCase().includes(searchTerm.toLowerCase());
}
console.log(searchCaseInsensitive('hello', 'Hello World')); // true
Searching and Finding
// indexOf() - first occurrence
let text = 'Hello World Hello';
console.log(text.indexOf('Hello')); // 0
console.log(text.indexOf('World')); // 6
console.log(text.indexOf('xyz')); // -1 (not found)
// lastIndexOf() - last occurrence
console.log(text.lastIndexOf('Hello')); // 12
// includes() - check if substring exists (ES6)
console.log(text.includes('World')); // true
console.log(text.includes('xyz')); // false
// startsWith() - check if string starts with (ES6)
console.log(text.startsWith('Hello')); // true
console.log(text.startsWith('World')); // false
// endsWith() - check if string ends with (ES6)
console.log(text.endsWith('Hello')); // true
console.log(text.endsWith('World')); // false
// search() - regular expression search
console.log(text.search(/World/)); // 6
console.log(text.search(/world/i)); // 6 (case insensitive)
Extracting Substrings
// slice() - extract substring
let text = 'Hello World';
console.log(text.slice(0, 5)); // 'Hello'
console.log(text.slice(6)); // 'World'
console.log(text.slice(-5)); // 'World'
console.log(text.slice(-5, -1)); // 'Worl'
// substring() - similar to slice but handles negative differently
console.log(text.substring(0, 5)); // 'Hello'
console.log(text.substring(6)); // 'World'
// substr() - deprecated but still used
console.log(text.substr(0, 5)); // 'Hello'
// split() - split string into array
let csv = 'apple,banana,orange';
let fruits = csv.split(','); // ['apple', 'banana', 'orange']
// Split with limit
let parts = 'a,b,c,d'.split(',', 2); // ['a', 'b']
// Split with regex
let words = 'Hello World'.split(/\s+/); // ['Hello', 'World']
String Manipulation
Methods for modifying and transforming strings.
Replacing Content
// replace() - replace first occurrence
let text = 'Hello World Hello';
console.log(text.replace('Hello', 'Hi')); // 'Hi World Hello'
// replace() with regex (global)
console.log(text.replace(/Hello/g, 'Hi')); // 'Hi World Hi'
// replace() with function
let replaced = text.replace(/Hello/g, (match, offset, string) => {
return match.toUpperCase();
});
console.log(replaced); // 'HELLO World HELLO'
// replaceAll() - replace all occurrences (ES2021)
console.log(text.replaceAll('Hello', 'Hi')); // 'Hi World Hi'
// Practical usage
function censorWords(text, badWords) {
let censored = text;
badWords.forEach(word => {
const regex = new RegExp(word, 'gi');
censored = censored.replace(regex, match => '*'.repeat(match.length));
});
return censored;
}
let message = 'This is bad and terrible';
console.log(censorWords(message, ['bad', 'terrible']));
// 'This is *** and ********'
Trimming and Padding
// trim() - remove whitespace from both ends
let text = ' Hello World ';
console.log(text.trim()); // 'Hello World'
// trimStart() - remove from start (ES2019)
console.log(text.trimStart()); // 'Hello World '
// trimEnd() - remove from end (ES2019)
console.log(text.trimEnd()); // ' Hello World'
// padStart() - pad at start (ES2017)
let num = '5';
console.log(num.padStart(3, '0')); // '005'
console.log('test'.padStart(10, '.')); // '......test'
// padEnd() - pad at end (ES2017)
console.log(num.padEnd(3, '0')); // '500'
console.log('test'.padEnd(10, '.')); // 'test......'
// Practical usage
function formatId(id, length = 8) {
return String(id).padStart(length, '0');
}
console.log(formatId(123)); // '00000123'
console.log(formatId(4567, 6)); // '004567'
Repeating and Reversing
// repeat() - repeat string (ES6)
console.log('abc'.repeat(3)); // 'abcabcabc'
console.log('*'.repeat(5)); // '*****'
// Reverse string (modern)
let text = 'Hello';
console.log(text.split('').reverse().join('')); // 'olleH'
// Reverse with spread operator
console.log([...text].reverse().join('')); // 'olleH'
// Reverse with for loop
function reverseString(str) {
let reversed = '';
for (let i = str.length - 1; i >= 0; i--) {
reversed += str[i];
}
return reversed;
}
// Check if string is palindrome
function isPalindrome(str) {
const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
return cleaned === cleaned.split('').reverse().join('');
}
console.log(isPalindrome('A man, a plan, a canal: Panama')); // true
Template Literals
Template literals provide enhanced string capabilities with interpolation and multi-line support.
String Interpolation
// Basic interpolation
let name = 'John';
let age = 30;
let message = `My name is ${name} and I am ${age} years old`;
console.log(message); // "My name is John and I am 30 years old"
// Expressions in template literals
let price = 19.99;
let tax = 0.08;
let total = `Total: $${(price * (1 + tax)).toFixed(2)}`;
console.log(total); // "Total: $21.59"
// Function calls
function formatName(first, last) {
return `${last}, ${first}`;
}
let fullName = formatName('John', 'Doe');
console.log(fullName); // "Doe, John"
// Property access
let user = { name: 'John', age: 30 };
let greeting = `Hello, ${user.name}!`;
console.log(greeting); // "Hello, John!"
Multi-line Strings
// Multi-line template literal
let html = `
Welcome
This is a multi-line string
`;
console.log(html);
// Preserves whitespace and newlines
let formatted = `
First line
Indented line
Last line
`;
// SQL queries
let query = `
SELECT * FROM users
WHERE age > ${minAge}
AND status = 'active'
ORDER BY name
`;
Tagged Template Literals
// Tagged template function
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
const value = values[i] ? `${values[i]}` : '';
return result + string + value;
}, '');
}
let name = 'John';
let age = 30;
let highlighted = highlight`Name: ${name}, Age: ${age}`;
// Result: "Name: John, Age: 30"
// HTML escaping
function html(strings, ...values) {
return strings.reduce((result, string, i) => {
const value = values[i] ?
String(values[i])
.replace(/&/g, '&')
.replace(//g, '>') : '';
return result + string + value;
}, '');
}
let userInput = '';
let safeHtml = html`User input: ${userInput}`;
// "User input: <script>alert("xss")</script>"
Regular Expressions
Regular expressions provide powerful pattern matching capabilities for strings.
Basic Regex Patterns
// Creating regex
let pattern1 = /hello/; // Literal
let pattern2 = new RegExp('hello'); // Constructor
let pattern3 = /hello/i; // With flags (i = case insensitive)
// test() - check if pattern matches
console.log(/hello/.test('hello world')); // true
console.log(/hello/.test('Hello World')); // false
console.log(/hello/i.test('Hello World')); // true (case insensitive)
// exec() - execute and return match details
let result = /(\w+)\s+(\w+)/.exec('John Doe');
console.log(result);
// ['John Doe', 'John', 'Doe', index: 0, input: 'John Doe']
// Global regex with exec
let pattern = /\b\w{4}\b/g;
let text = 'This test word four word';
let match;
while ((match = pattern.exec(text)) !== null) {
console.log(`Found ${match[0]} at index ${match.index}`);
}
String Methods with Regex
// match() - find all matches
let text = 'The rain in Spain falls mainly in the plain';
let result = text.match(/ain/g);
console.log(result); // ['ain', 'ain', 'ain', 'ain']
// match() with capture groups
let result2 = text.match(/(\w+)\s+(\w+)/);
console.log(result2); // ['The rain', 'The', 'rain']
// replace() with regex and capture groups
let formatted = 'John Doe'.replace(/(\w+)\s+(\w+)/, '$2, $1');
console.log(formatted); // 'Doe, John'
// split() with regex
let data = 'name=John&age=30&city=NYC';
let pairs = data.split(/&/);
console.log(pairs); // ['name=John', 'age=30', 'city=NYC']
// Practical email validation
function isValidEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
console.log(isValidEmail('john@example.com')); // true
console.log(isValidEmail('invalid-email')); // false
Unicode and Internationalization
JavaScript provides good support for Unicode and international characters.
Unicode Support
// Unicode characters
let unicode = 'Hello 🌍 World';
console.log(unicode.length); // 14 (emoji counts as 2)
// codePointAt() for full Unicode support
console.log(unicode.codePointAt(6)); // 127757 (globe emoji)
// fromCodePoint() to create Unicode strings
let emoji = String.fromCodePoint(127757); // 🌍
// Spread operator with Unicode
let chars = [...unicode];
console.log(chars.length); // 13 (correct character count)
// for...of with Unicode
for (const char of unicode) {
console.log(char);
}
// Normalization
let str1 = 'café';
let str2 = 'cafe\u0301'; // Same visual character
console.log(str1 === str2); // false
console.log(str1.normalize() === str2.normalize()); // true
// Normalization forms
console.log(str1.normalize('NFC')); // Canonical decomposition
console.log(str1.normalize('NFD')); // Canonical composition
International Methods
// Locale-specific comparison
let strings = ['Zebra', 'apple', 'Banana'];
strings.sort((a, b) => a.localeCompare(b));
// ['apple', 'Banana', 'Zebra'] (correct alphabetical order)
// Locale-specific case conversion
let turkish = 'i';
console.log(turkish.toLocaleUpperCase('tr')); // 'İ' (Turkish dotted I)
// String formatting with Intl
let number = 1234567.89;
let formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
console.log(formatter.format(number)); // '$1,234,567.89'
// Date formatting
let date = new Date();
let dateFormatter = new Intl.DateTimeFormat('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
console.log(dateFormatter.format(date)); // "Monday, January 1, 2023"
String Performance
Understanding performance characteristics of string operations.
Efficient String Building
// Inefficient string concatenation in loops
let result = '';
for (let i = 0; i < 10000; i++) {
result += i; // Creates new string each iteration
}
// Efficient with array join
let parts = [];
for (let i = 0; i < 10000; i++) {
parts.push(i);
}
let result = parts.join('');
// Efficient with template literals and reduce
let numbers = Array.from({length: 10000}, (_, i) => i);
let result = numbers.reduce((str, num) => `${str}${num}`, '');
// Modern: using String.fromCodePoint
let chars = [];
for (let i = 65; i <= 90; i++) { // A-Z
chars.push(String.fromCodePoint(i));
}
let alphabet = chars.join('');
Optimization Techniques
// Cache string lengths
function processLongString(str) {
const len = str.length;
for (let i = 0; i < len; i++) {
// Process character at i
}
}
// Use appropriate methods
// For simple existence check: includes() vs indexOf()
if (str.includes('substring')) { /* faster and clearer */ }
// For position: indexOf() or search()
const position = str.indexOf('find');
// For pattern matching: use regex directly
const regex = /pattern/g;
const matches = str.match(regex);
// Avoid unnecessary string creation
// Bad:
if (str.toString().toLowerCase() === 'test') { }
// Good:
if (str.toLowerCase() === 'test') { }
Practical String Utilities
Common utility functions for string manipulation.
String Utility Functions
// Capitalize first letter
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// Title case
function titleCase(str) {
return str.toLowerCase().split(' ').map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
).join(' ');
}
// Camel case
function camelCase(str) {
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
return index === 0 ? word.toLowerCase() : word.toUpperCase();
}).replace(/\s+/g, '');
}
// Kebab case
function kebabCase(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/[\s_]+/g, '-')
.toLowerCase();
}
// Slugify
function slugify(str) {
return str.toLowerCase()
.replace(/[^\w\s-]/g, '') // Remove special chars
.replace(/[\s_-]+/g, '-') // Replace spaces with hyphens
.replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens
}
// Truncate with ellipsis
function truncate(str, length, suffix = '...') {
if (str.length <= length) return str;
return str.slice(0, length - suffix.length) + suffix;
}
// Word wrap
function wordWrap(str, maxLength) {
const words = str.split(' ');
const lines = [];
let currentLine = '';
words.forEach(word => {
if ((currentLine + word).length <= maxLength) {
currentLine += (currentLine ? ' ' : '') + word;
} else {
lines.push(currentLine);
currentLine = word;
}
});
if (currentLine) lines.push(currentLine);
return lines.join('\n');
}
Validation Functions
// Email validation
function isValidEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
// Phone number validation
function isValidPhone(phone) {
const regex = /^\+?[\d\s-()]{10,}$/;
return regex.test(phone);
}
// URL validation
function isValidUrl(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
}
// Strong password validation
function isStrongPassword(password) {
const hasLength = password.length >= 8;
const hasUpper = /[A-Z]/.test(password);
const hasLower = /[a-z]/.test(password);
const hasNumber = /\d/.test(password);
const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(password);
return hasLength && hasUpper && hasLower && hasNumber && hasSpecial;
}
// Credit card validation (Luhn algorithm)
function isValidCreditCard(number) {
const digits = number.replace(/\D/g, '');
let sum = 0;
let isEven = false;
for (let i = digits.length - 1; i >= 0; i--) {
let digit = parseInt(digits[i], 10);
if (isEven) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
isEven = !isEven;
}
return sum % 10 === 0;
}
Best Practices
Guidelines for working with strings effectively and safely.
Security Considerations
// Always sanitize user input
function sanitizeInput(input) {
return input
.replace(/[<>]/g, '') // Remove HTML tags
.trim()
.slice(0, 1000); // Limit length
}
// Use textContent instead of innerHTML for user content
element.textContent = userInput; // Safe
// element.innerHTML = userInput; // Dangerous (XSS risk)
// Escape HTML when necessary
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Use template literals for building HTML
const safeHtml = `${escapeHtml(userInput)}`;
Code Organization
// Use constants for repeated strings
const API_URL = 'https://api.example.com';
const ERROR_MESSAGE = 'An error occurred';
// Extract string logic into functions
function formatCurrency(amount) {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(amount);
}
// Use object for string collections
const MESSAGES = {
SUCCESS: 'Operation completed successfully',
ERROR: 'An error occurred',
LOADING: 'Loading...',
EMPTY: 'No data available'
};
// Use template literals for dynamic content
function createUserCard(user) {
return `
${escapeHtml(user.name)}
${escapeHtml(user.email)}
Member since ${new Date(user.joinDate).toLocaleDateString()}
`;
}
Common Pitfalls
Avoid these common mistakes when working with strings.
Unicode Issues
// Length confusion with Unicode
let emoji = '👋';
console.log(emoji.length); // 2 (wrong)
console.log([...emoji].length); // 1 (correct)
// Character access issues
console.log(emoji[0]); // '�' (broken)
console.log(emoji.codePointAt(0)); // 128075 (correct)
// Always use for...of for Unicode iteration
for (const char of emoji) {
console.log(char); // '👋'
}
// Normalize Unicode strings
function normalizeString(str) {
return str.normalize('NFC');
}
Performance Pitfalls
// String concatenation in loops
// Bad:
let result = '';
for (let i = 0; i < 1000; i++) {
result += items[i]; // Creates new string each time
}
// Good:
let parts = [];
for (let i = 0; i < 1000; i++) {
parts.push(items[i]);
}
let result = parts.join('');
// Unnecessary string operations
// Bad:
if (str.toString().toLowerCase().includes('test')) { }
// Good:
if (str.toLowerCase().includes('test')) { }
// Regex in loops
// Bad:
for (const item of items) {
if (/pattern/.test(item)) { /* ... */ }
}
// Good:
const regex = /pattern/;
for (const item of items) {
if (regex.test(item)) { /* ... */ }
}
Summary
Key Takeaways
- Strings are immutable in JavaScript
- Template literals provide powerful interpolation
- Regular expressions enable pattern matching
- Unicode support requires special consideration
- String methods return new strings, don't modify originals
Best Practices
- Use template literals for string interpolation
- Prefer includes() over indexOf() for existence checks
- Handle Unicode properly with spread operator and for...of
- Use array join for building strings in loops
- Always sanitize user input for security
Common Pitfalls
- Confusing string length with Unicode characters
- Inefficient string concatenation in loops
- Not escaping user input (XSS risk)
- Using deprecated methods like substr()
- Creating regex in loops unnecessarily