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

🧠 Test Your Knowledge

What does the ?? operator do?