Modern Features
Template Literals
Basic Template Literals
const name = 'John';
const age = 30;
// Basic template
const greeting = `Hello, ${name}!`;
// Multi-line strings
const html = `
<div class="user-card">
<h2>${name}</h2>
<p>Age: ${age}</p>
<p>Status: ${age >= 18 ? 'Adult' : 'Minor'}</p>
</div>
`;
// Expression evaluation
const mathResult = `The result is: ${2 + 3 * 4}`; // "The result is: 14"
// Function calls
const formatted = `Today is ${new Date().toLocaleDateString()}`;
Tagged Template Literals
// Basic tagged template
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
const value = values[i] ? `<mark>${values[i]}</mark>` : '';
return result + string + value;
}, '');
}
const name = 'John';
const age = 30;
const highlighted = highlight`Hello ${name}, you are ${age} years old`;
// Result: "Hello <mark>John</mark>, you are <mark>30</mark> years old"
// Advanced tagged template for SQL-like queries
function sql(strings, ...values) {
const query = strings.reduce((result, string, i) => {
const value = values[i] ? `'${values[i]}'` : '';
return result + string + value;
}, '');
return {
query,
execute: () => console.log('Executing:', query),
};
}
const userId = 123;
const userName = 'John';
const query = sql`SELECT * FROM users WHERE id = ${userId} AND name = ${userName}`;
query.execute();
// Styled components pattern
function styled(strings, ...values) {
const css = strings.reduce((result, string, i) => {
const value = values[i] || '';
return result + string + value;
}, '');
return element => {
element.style.cssText = css;
return element;
};
}
const primaryColor = '#007bff';
const buttonStyles = styled`
background-color: ${primaryColor};
border: none;
padding: 10px 20px;
border-radius: 4px;
color: white;
`;
Destructuring
Array Destructuring
const numbers = [1, 2, 3, 4, 5];
// Basic destructuring
const [first, second] = numbers; // first = 1, second = 2
const [a, , c] = numbers; // Skip second element: a = 1, c = 3
const [x, y, ...rest] = numbers; // rest = [3, 4, 5]
// Default values
const [p = 10, q = 20, r = 30] = [1]; // p = 1, q = 20, r = 30
// Swapping variables
let m = 1,
n = 2;
[m, n] = [n, m]; // m = 2, n = 1
// Nested destructuring
const nested = [
[1, 2],
[3, 4],
];
const [[a1, a2], [b1, b2]] = nested;
// Function return values
function getCoordinates() {
return [10, 20];
}
const [x, y] = getCoordinates();
// Ignoring values
const [first, , , fourth] = [1, 2, 3, 4]; // first = 1, fourth = 4
Object Destructuring
const person = {
name: 'John',
age: 30,
city: 'New York',
country: 'USA',
};
// Basic destructuring
const { name, age } = person;
// Rename variables
const { name: fullName, age: years } = person;
// Default values
const { name, age, profession = 'Unknown' } = person;
// Rest operator
const { name, ...details } = person; // details = {age: 30, city: 'New York', country: 'USA'}
// Nested destructuring
const user = {
id: 1,
profile: {
name: 'John',
address: {
street: '123 Main St',
city: 'New York',
},
},
};
const {
profile: {
name,
address: { city },
},
} = user;
// Function parameters
function createUser({ name, age, email = 'unknown@example.com' }) {
return { name, age, email };
}
createUser({ name: 'John', age: 30 });
// Computed property names
const prop = 'name';
const { [prop]: value } = person; // value = 'John'
Spread Operator
Array Spread
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// Combine arrays
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
const withAdditional = [0, ...arr1, 3.5, ...arr2, 7]; // [0, 1, 2, 3, 3.5, 4, 5, 6, 7]
// Copy array
const copy = [...arr1]; // [1, 2, 3]
// Convert NodeList to Array
const elements = [...document.querySelectorAll('div')];
// Convert string to character array
const chars = [...'hello']; // ['h', 'e', 'l', 'l', 'o']
// Find max/min in array
const numbers = [1, 5, 3, 9, 2];
const max = Math.max(...numbers); // 9
const min = Math.min(...numbers); // 1
// Function arguments
function sum(a, b, c) {
return a + b + c;
}
sum(...arr1); // 6
Object Spread
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
// Combine objects
const merged = { ...obj1, ...obj2 }; // {a: 1, b: 2, c: 3, d: 4}
// Override properties
const overridden = { ...obj1, b: 20, e: 5 }; // {a: 1, b: 20, e: 5}
// Copy object
const copy = { ...obj1 }; // {a: 1, b: 2}
// Add properties to existing object
const person = { name: 'John', age: 30 };
const employee = {
...person,
id: 123,
department: 'Engineering',
};
// Conditional properties
const includeAge = true;
const user = {
name: 'John',
...(includeAge && { age: 30 }),
email: 'john@example.com',
};
// Remove properties (with destructuring)
const { password, ...publicUser } = {
id: 1,
name: 'John',
email: 'john@example.com',
password: 'secret',
};
// publicUser = {id: 1, name: 'John', email: 'john@example.com'}
Optional Chaining & Nullish Coalescing
Optional Chaining (?.)
const user = {
id: 1,
name: 'John',
profile: {
avatar: 'avatar.jpg',
settings: {
theme: 'dark',
},
},
friends: [{ name: 'Alice' }, { name: 'Bob' }],
};
// Safe property access
const theme = user?.profile?.settings?.theme; // 'dark'
const missing = user?.profile?.missing?.value; // undefined
// Safe method calls
const upperName = user?.getName?.(); // undefined (method doesn't exist)
const validCall = user?.profile?.getTheme?.(); // undefined
// Safe array access
const firstFriend = user?.friends?.[0]; // {name: 'Alice'}
const tenthFriend = user?.friends?.[10]; // undefined
const friendName = user?.friends?.[0]?.name; // 'Alice'
// Dynamic property access
const property = 'theme';
const value = user?.profile?.settings?.[property]; // 'dark'
// Real-world examples
function displayUserInfo(user) {
const avatar = user?.profile?.avatar ?? 'default-avatar.jpg';
const theme = user?.profile?.settings?.theme ?? 'light';
const friendCount = user?.friends?.length ?? 0;
return {
avatar,
theme,
friendCount,
};
}
// API response handling
async function fetchUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
return {
name: data?.user?.name ?? 'Unknown',
email: data?.user?.contact?.email ?? 'No email',
isActive: data?.user?.status?.active ?? false,
};
}
Nullish Coalescing (??)
// Nullish coalescing - only null/undefined trigger default
const value1 = null ?? 'default'; // 'default'
const value2 = undefined ?? 'default'; // 'default'
const value3 = 0 ?? 'default'; // 0
const value4 = '' ?? 'default'; // ''
const value5 = false ?? 'default'; // false
// Compare with logical OR (||)
const orValue1 = 0 || 'default'; // 'default' (falsy triggers default)
const orValue2 = '' || 'default'; // 'default' (falsy triggers default)
const nullishValue1 = 0 ?? 'default'; // 0 (only null/undefined trigger default)
const nullishValue2 = '' ?? 'default'; // ''
// Configuration with defaults
function createConfig(options = {}) {
return {
host: options.host ?? 'localhost',
port: options.port ?? 3000,
debug: options.debug ?? false,
timeout: options.timeout ?? 0, // 0 is valid timeout
retries: options.retries ?? 3,
};
}
// Chain with optional chaining
const config = {
database: {
host: 'localhost',
// port is missing
},
};
const dbHost = config?.database?.host ?? 'default-host';
const dbPort = config?.database?.port ?? 5432;
const dbName = config?.database?.name ?? 'myapp';
Logical Assignment Operators
Logical Assignment (ES2021)
// Nullish assignment (??=)
let user = null;
user ??= { name: 'Default User' }; // Assigns only if user is null/undefined
console.log(user); // {name: 'Default User'}
let existingUser = { name: 'John' };
existingUser ??= { name: 'Default User' }; // No assignment (existingUser is not null/undefined)
console.log(existingUser); // {name: 'John'}
// Logical OR assignment (||=)
let message = '';
message ||= 'Default message'; // Assigns if message is falsy
console.log(message); // 'Default message'
let existingMessage = 'Hello';
existingMessage ||= 'Default message'; // No assignment (existingMessage is truthy)
console.log(existingMessage); // 'Hello'
// Logical AND assignment (&&=)
let config = { debug: true };
config.debug &&= false; // Assigns if config.debug is truthy
console.log(config.debug); // false
let emptyConfig = {};
emptyConfig.debug &&= false; // No assignment (debug is undefined)
console.log(emptyConfig.debug); // undefined
// Practical examples
class Cache {
constructor() {
this.data = {};
}
get(key) {
// Initialize with empty array if not exists
this.data[key] ??= [];
return this.data[key];
}
addItem(key, item) {
this.get(key).push(item);
}
}
// Settings management
class Settings {
constructor(initialSettings = {}) {
this.settings = initialSettings;
}
updateSetting(key, value) {
// Only update if current value is falsy
this.settings[key] ||= value;
}
enableFeature(feature) {
// Only enable if currently true
this.settings[feature] &&= true;
}
}
Advanced Destructuring Patterns
Function Parameter Destructuring
// API endpoint handler
function createUser({ name, email, age = 18, role = 'user', ...metadata }) {
return {
id: generateId(),
name,
email,
age,
role,
metadata,
createdAt: new Date(),
};
}
// React component pattern
function UserCard({
user: { name, email, avatar },
showEmail = true,
className = 'user-card',
onClick = () => {},
}) {
return {
name,
email: showEmail ? email : null,
avatar,
className,
onClick,
};
}
// Array destructuring in function parameters
function calculateDistance([x1, y1], [x2, y2]) {
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
}
calculateDistance([0, 0], [3, 4]); // 5
Loop Destructuring
const users = [
{ id: 1, name: 'John', email: 'john@example.com' },
{ id: 2, name: 'Jane', email: 'jane@example.com' },
];
// For...of with destructuring
for (const { id, name } of users) {
console.log(`User ${id}: ${name}`);
}
// Array entries
for (const [index, { name }] of users.entries()) {
console.log(`${index}: ${name}`);
}
// Object entries
const settings = {
theme: 'dark',
language: 'en',
notifications: true,
};
for (const [key, value] of Object.entries(settings)) {
console.log(`${key}: ${value}`);
}
// Map destructuring
const userMap = new Map([
['john', { name: 'John', age: 30 }],
['jane', { name: 'Jane', age: 25 }],
]);
for (const [username, { name, age }] of userMap) {
console.log(`${username}: ${name} (${age})`);
}
Modern Array/Object Methods
Array Methods
// Array.from with mapper
const squares = Array.from({ length: 5 }, (_, i) => i * i); // [0, 1, 4, 9, 16]
// Array.from with Set
const uniqueNumbers = Array.from(new Set([1, 2, 2, 3, 3])); // [1, 2, 3]
// flatMap
const sentences = ['Hello world', 'How are you'];
const words = sentences.flatMap(sentence => sentence.split(' '));
// ['Hello', 'world', 'How', 'are', 'you']
// at() method (ES2022)
const arr = [1, 2, 3, 4, 5];
arr.at(-1); // 5 (last element)
arr.at(-2); // 4 (second to last)
Object Methods
// Object.fromEntries
const entries = [
['name', 'John'],
['age', 30],
];
const obj = Object.fromEntries(entries); // {name: 'John', age: 30}
// Transform object
const user = { name: 'john', age: 30, email: 'JOHN@EXAMPLE.COM' };
const normalized = Object.fromEntries(
Object.entries(user).map(([key, value]) => [
key,
typeof value === 'string' ? value.toLowerCase() : value,
])
);
// Object.hasOwn (ES2022)
const obj = { name: 'John' };
Object.hasOwn(obj, 'name'); // true
Object.hasOwn(obj, 'toString'); // false (inherited)
// Compare with hasOwnProperty
obj.hasOwnProperty('name'); // true
obj.hasOwnProperty('toString'); // false
String Methods
// String.prototype.replaceAll (ES2021)
const text = 'Hello world, wonderful world';
text.replaceAll('world', 'universe'); // 'Hello universe, wonderful universe'
// padStart/padEnd
const id = '123';
id.padStart(6, '0'); // '000123'
id.padEnd(6, '-'); // '123---'
// includes/startsWith/endsWith
const url = 'https://example.com/api/users';
url.startsWith('https://'); // true
url.endsWith('/users'); // true
url.includes('/api/'); // true