Dec 19, 2025

Modern ES6+ JavaScript: The Practical Upgrade Your Code Deserves

Tags

There was a moment when I opened an old JavaScript file full of var, long anonymous callbacks, and string concatenations… and honestly, it felt like reading ancient text. After switching that project to ES6+ features, the code became shorter, clearer, and way less bug-prone.


If you’ve ever felt your JS looks “heavy” or hard to follow, modern ES6+ syntax is the upgrade your brain will thank you for.


Below is a simple walkthrough of the ES6+ features that actually matter in real apps, whether you’re working in React, Angular, or plain JavaScript.

es6-plus-features

Why ES6+ Changes How We Write JavaScript


ES6+ (also called “modern JavaScript”) isn’t just about fancy syntax. It solves real pain:
  • Accidental bugs from var and weird scoping.
  • Messy string concatenation.
  • Callback hell and unreadable async code.
  • Huge objects and arrays that are annoying to update.
Think of ES6+ as a toolkit that makes your code read like a story instead of a puzzle.


let and const: Fewer “Why is this value changing?” Moments


Old JavaScript used var everywhere, which is function-scoped and can be re-declared accidentally. That leads to sneaky bugs. ES6 gave us:

  • let – changeable variable.
  • const – value cannot be reassigned.

let counter = 0;
counter = 1; // fine

const apiUrl = 'https://api.example.com';
// apiUrl = '...'  not allowed


In React and Angular, a simple rule helps a lot:
  • Use const by default
  • Only use let when you really need to reassign.
This makes your code more predictable. Read more about let & const variables, check out this article.


Arrow Functions: Short and Sweet


Arrow functions are just functions with less noise:

// Before
function double(x) {
  return x * 2;
}

// ES6+
const double = x => x * 2;


In React:

const UserList = ({ users }) => (
  <ul>
    {users.map(user => <li key={user.id}>{user.name}</li>)}
  </ul>
);

Arrow functions are perfect for array methods like mapfilter, and reduce, and they help avoid confusion with this in callbacks.


Template Literals: Strings That Don’t Hurt


No more "Hello " + name + "!" chaos.

const name = 'Alice';
const greeting = `Hello, ${name}!`;

You can even create multi-line strings without ugly \n everywhere. This is great for logging, debugging, and dynamic messages in both React and Angular apps.


Destructuring: Pick Only What You Need


Destructuring lets you “unpack” objects and arrays in one line.

const user = { id: 1, name: 'Alice', role: 'admin' };
const { name, role } = user;
// name = 'Alice', role = 'admin'


React props:

const UserCard = ({ name, role }) => (
  <p>{name} – {role}</p>
);


Angular:

const { id } = this.route.snapshot.params;

It keeps your code focused and avoids repeating user.name, user.role all over the place.


Rest and Spread: The “…” You See Everywhere


That triple dot ... is one of the most powerful additions.

Spread (copy/merge):

const baseUser = { name: 'Alice', role: 'user' };
const adminUser = { ...baseUser, role: 'admin' }; // override role

const numbers = [1, 2];
const more = [...numbers, 3, 4]; // [1, 2, 3, 4]


Rest (collect “the rest”):

const { password, ...safeUser } = user; // everything except password

function logAll(first, ...rest) {
  console.log(first, rest);
}

In React and Angular, spread is your best friend for immutable state updates. We have a detailed article on this topic, "Spread vs Rest in JavaScript: Understand the Difference with Real-World Examples" Don't forget to check it out.


Default Parameters: Safer Functions


You no longer need manual “fallback” logic:

function greet(name = 'Guest') {
  return `Hello, ${name}!`;
}

greet();       // "Hello, Guest!"
greet('Sam');  // "Hello, Sam!"

This helps when you call functions from multiple places and don’t want to crash if a value is missing.


Classes: Cleaner Structure for Components and Services


ES6 classes make object-oriented patterns less painful:

class User {
  constructor(name) {
    this.name = name;
  }

  greet() {
    return `Hi, I'm ${this.name}`;
  }
}

const user = new User('Alice');
user.greet(); // "Hi, I'm Alice"

Angular leans on classes heavily (components, services). ES6 classes just make that pattern more obvious and readable.


Modules: import / export for Real Projects


Modules let you split logic into files and reuse code easily:

// userService.js
export function getUser() { /* ... */ }
export const USER_ROLES = ['admin', 'user'];

// main.js
import { getUser, USER_ROLES } from './userService.js';

React components, Angular services, utility functions—all of these benefit from clean module boundaries. To know more, check our article JavaScript CommonJS vs ES Modules: Import, Export, Migration, and Developer Best Practices.


Async/Await: No More Callback Hell


This is one of the biggest quality-of-life improvements.

Before:

fetch('/api/users')
  .then(res => res.json())
  .then(users => console.log(users));


With async/await:

async function loadUsers() {
  const res = await fetch('/api/users');
  const users = await res.json();
  console.log(users);
}

In React and Angular, this makes side effects and data fetching much easier to follow and debug.


Bonus: Optional Chaining and Nullish Coalescing


These two tiny features remove a lot of “if checks.”

Optional chaining (?.):

const city = user.address?.city;
// No error if address is undefined or null


Nullish coalescing (??):

const count = input ?? 0; // Use 0 only if input is null or undefined

Perfect for working with API data where some fields might be missing. Recommendation: What is a Nullish coalescing operator '??'

Conclusion: ES6+ Is Not “Fancy” — It’s Practical


When ES6+ first came out, it felt like “extra” syntax. But after using it in real Angular and React projects, it stopped being “nice-to-have” and became “how did I ever live without this?”

  • let/const make bugs easier to avoid.
  • Arrow functions and destructuring make code shorter and clearer.
  • Spread/rest make state updates predictable.
  • Async/await makes async code readable.
  • Optional chaining and defaults make app code safer.

If your code still looks like classic ES5, you don’t have to rewrite everything overnight. Pick one feature at a time—maybe start with const, arrow functions, and template literals—and slowly modernise. Your future self (and your teammates) will be very grateful.


EmoticonEmoticon