JavaScript 2020 (ES2020)
Modern JavaScript features and improvements
🚀 JavaScript ES2020 Features
ES2020 brought powerful new features to JavaScript, making code more readable and efficient. Let's explore the key additions!
// ES2020 example - Optional Chaining
const user = { profile: { name: "Alice" } };
console.log(user?.profile?.name); // "Alice"
console.log(user?.address?.street); // undefined (no error!)
Major ES2020 Features
Optional Chaining
Safe property access
obj?.prop?.method?.()
Nullish Coalescing
Better default values
const value = input ?? "default";
BigInt
Handle large numbers
const big = 123456789012345678901234567890n;
Dynamic Import
Load modules on demand
const module = await import('./utils.js');
🔹 Optional Chaining (?.)
Safely access nested object properties without errors:
// Before ES2020 - risky!
const user = { profile: { name: "Alice" } };
// This could throw an error if profile doesn't exist
// const name = user.profile.name;
// ES2020 - Safe way
const userName = user?.profile?.name;
const userAge = user?.profile?.age ?? "Not specified";
const userEmail = user?.contact?.email ?? "No email";
console.log("Name:", userName); // "Alice"
console.log("Age:", userAge); // "Not specified"
console.log("Email:", userEmail); // "No email"
// Works with methods too!
const result = user?.profile?.getName?.();
console.log("Method result:", result ?? "Method not found");
Output:
Name: Alice
Age: Not specified
Email: No email
Method result: Method not found
🔹 Nullish Coalescing (??)
Better way to provide default values:
// The problem with || operator
let count = 0;
let message = "";
let isActive = false;
// Using || (not always what we want)
console.log("Count with ||:", count || 10); // 10 (we wanted 0!)
console.log("Message with ||:", message || "Hello"); // "Hello" (we wanted "")
// Using ?? (nullish coalescing) - only null/undefined trigger default
console.log("Count with ??:", count ?? 10); // 0 (correct!)
console.log("Message with ??:", message ?? "Hello"); // "" (correct!)
// Practical examples
const config = {
timeout: 0, // 0 is a valid timeout
retries: null, // null means use default
debug: false // false is a valid setting
};
const timeout = config.timeout ?? 5000; // 0 (keeps the 0)
const retries = config.retries ?? 3; // 3 (null triggers default)
const debug = config.debug ?? true; // false (keeps false)
console.log("Timeout:", timeout);
console.log("Retries:", retries);
console.log("Debug:", debug);
Output:
Count with ||: 10
Message with ||: Hello
Count with ??: 0
Message with ??: ""
Timeout: 0
Retries: 3
Debug: false
🔹 BigInt for Large Numbers
Handle numbers larger than Number.MAX_SAFE_INTEGER:
// Regular numbers have limits
console.log("Max safe integer:", Number.MAX_SAFE_INTEGER);
// 9007199254740991
// Beyond this, precision is lost
console.log("Unsafe:", 9007199254740992 + 1); // 9007199254740992 (wrong!)
// BigInt can handle huge numbers
const bigNumber1 = 123456789012345678901234567890n;
const bigNumber2 = BigInt("987654321098765432109876543210");
console.log("Big number 1:", bigNumber1);
console.log("Big number 2:", bigNumber2);
// BigInt operations
const sum = bigNumber1 + bigNumber2;
const product = bigNumber1 * 2n; // Note: use 2n, not 2
console.log("Sum:", sum);
console.log("Product:", product);
// Convert between BigInt and regular numbers
const regularNumber = 42;
const bigFromRegular = BigInt(regularNumber);
const backToRegular = Number(bigFromRegular);
console.log("Converted:", bigFromRegular, "back to:", backToRegular);
Output:
Max safe integer: 9007199254740991
Unsafe: 9007199254740992
Big number 1: 123456789012345678901234567890n
Big number 2: 987654321098765432109876543210n
Sum: 1111111110111111111011111111100n
Converted: 42n back to: 42
🔹 Dynamic Import
Load JavaScript modules when you need them:
// Traditional import (loads immediately)
// import { utils } from './utils.js';
// Dynamic import (loads when needed)
async function loadUtils() {
try {
// Load module only when this function is called
const utilsModule = await import('./utils.js');
// Use the imported functions
const result = utilsModule.formatDate(new Date());
console.log("Formatted date:", result);
return utilsModule;
} catch (error) {
console.error("Failed to load utils:", error);
}
}
// Load module based on user action
document.getElementById('loadButton')?.addEventListener('click', async () => {
const utils = await loadUtils();
if (utils) {
console.log("Utils loaded successfully!");
}
});
// Conditional loading
async function loadFeature(featureName) {
if (featureName === 'charts') {
const chartModule = await import('./charts.js');
return chartModule.createChart;
} else if (featureName === 'maps') {
const mapModule = await import('./maps.js');
return mapModule.initMap;
}
}
// Example usage
loadFeature('charts').then(createChart => {
if (createChart) {
console.log("Chart feature loaded!");
}
});
Benefits:
✅ Faster initial page load
✅ Load code only when needed
✅ Better performance for large apps
✅ Conditional feature loading