{}const=>[]async()letfn</>var
DevelopmentJS

ES6+ features: modern JavaScript for developers

A complete guide to modern JavaScript capabilities: arrow functions, destructuring, promises, async/await, classes, and other ES6+ features. Learn the key tools of modern JavaScript that will make your code cleaner and more efficient.

К

Kodik

Author

7 min read

Let and Const: a new way to declare variables

Before ES6, only the var keyword was used to declare variables, which had a number of problems. Modern JavaScript offers let and const with a block scope.

// var - functional scope
function oldWay() {
    if (true) {
        var x = 10;
    }
    console.log(x); // 10 - variable available
}

// let - block scope
function newWay() {
    if (true) {
        let y = 10;
    }
    // console.log(y); // ReferenceError
}

// const - for immutable references
const API_URL = 'https://api.example.com';
const user = { name: 'Alexey' };
user.name = 'Ivan'; // Working - object is changeable
// user = {}; // TypeError - cannot be reassigned

Use const by default for all variables that do not require reassignment. This makes the code more predictable and protects against accidental changes.

🔥 100,000+ students already with us

Tired of reading theory?
Time to code!

Kodik — an app where you learn to code through practice. AI mentor, interactive lessons, real projects.

🤖 AI 24/7
🎓 Certificates
💰 Free
🚀 Start learning
Joined today

Arrow functions: brevity and this

Arrow functions provide a more compact syntax and solve the problem with the context this.

// Classic function
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(n) {
    return n * 2;
});

// Switch function
const doubledArrow = numbers.map(n => n * 2);

// Multiple parameters
const sum = (a, b) => a + b;

// Function body with multiple expressions
const processUser = user => {
    const name = user.name.toUpperCase();
    return `Hi ${name}!`;
};

An important feature of arrow functions is that they do not create their own context this, but inherit it from the surrounding area.

class Timer {
    constructor() {
        this.seconds = 0;
    }
    
    // With the usual function, .bind(this) would be required
    start() {
        setInterval(() => {
            this.seconds++; // this indicates a Timer instance
            console.log(this.seconds);
        }, 1000);
    }
}

Destructuring: extracting data from objects and arrays

Destructuring allows you to extract values from arrays and objects into separate variables in an elegant way.

// Destruction of objects
const user = {
    name: 'Alexey',
    age: 30,
    email: 'alexey@example.com'
};

const { name, age } = user;
console.log(name); // Alexey

// Renaming variables
const { name: userName, age: userAge } = user;

// Default values
const { role = 'user' } = user;

// Destructuring of arrays
const colors = ['red', 'green', 'blue'];
const [primary, secondary] = colors;

// Skipping items
const [first, , third] = colors;

// Rest operator
const [head, ...tail] = colors;
console.log(tail); // ['green', 'blue']

Destructuring is especially convenient in function parameters.

function displayUser({ name, age, email = 'not specified' }) {
    console.log(`${name}, ${age} years old, email: ${email}`);
}

displayUser(user);

Spread and Rest operators: working with collections

The spread operator and the rest operator use the same syntax ..., but perform opposite operations.

// Spread - expands an array or object
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// Copying an array
const original = [1, 2, 3];
const copy = [...original];

// Spread for objects
const defaults = { theme: 'dark', language: 'ru' };
const userSettings = { language: 'en', fontSize: 14 };
const settings = { ...defaults, ...userSettings };
// { theme: 'dark', language: 'en', fontSize: 14 }

// Rest - collects the remaining elements
function sum(...numbers) {
    return numbers.reduce((total, n) => total + n, 0);
}

sum(1, 2, 3, 4); // 10

Template strings: convenient interpolation

Template literals use back quotes and allow you to embed expressions in a string.

const name = 'Alexey';
const age = 30;

// Old way
const greeting = 'Hi, ' + name + '! To you ' + age + ' years.';

// Template strings
const modernGreeting = `Hi ${name}! You are ${age} years old.`;

// Multi-line strings
const html = `
    <div class="card">
        <h2>${name}</h2>
        <p>Age: ${age}</p>
    </div>
`;

// Expressions in templates
const price = 100;
const message = `Discounted price: ${price * 0.9} rub.`;

Promises and async/await: asynchrony without pain

Promises have greatly simplified working with asynchronous code, and async/await have made it look like synchronous.

// Promis
function fetchUser(id) {
    return fetch(`/api/users/${id}`)
        .then(response => response.json())
        .then(data => data.user)
        .catch(error => console.error('Error:', error));
}

// Async/await - more readable code
async function fetchUserAsync(id) {
    try {
        const response = await fetch(`/api/users/${id}`);
        const data = await response.json();
        return data.user;
    } catch (error) {
        console.error('Error:', error);
    }
}

// Parallel execution
async function fetchMultipleUsers(ids) {
    const promises = ids.map(id => fetchUserAsync(id));
    const users = await Promise.all(promises);
    return users;
}

Modules: code organization

ES6 modules allow you to split code into logical parts and manage dependencies.

// math.js - export
export const PI = 3.14159;

export function sum(a, b) {
    return a + b;
}

export class Calculator {
    multiply(a, b) {
        return a * b;
    }
}

// main.js - import
import { PI, sum, Calculator } from './math.js';

// Import entire module
import * as MathUtils from './math.js';

// Default export
// user.js
export default class User {
    constructor(name) {
        this.name = name;
    }
}

// Import default export
import User from './user.js';

Classes: Object-Oriented Programming

ES6 added syntactic sugar for working with prototypes in the form of classes.

class Animal {
    constructor(name) {
        this.name = name;
    }
    
    speak() {
        console.log(`${this.name} makes a sound`);
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name); // Calling the parent constructor
        this.breed = breed;
    }
    
    speak() {
        console.log(`${this.name} is barking`);
    }
    
    // Static method
    static info() {
        return 'Dogs - pets';
    }
}

const dog = new Dog('Bobik', 'Labrador');
dog.speak(); // Bobik barks

Array methods: map, filter, reduce and others

Modern array methods make working with data more declarative.

const users = [
    { name: 'Alexey', age: 30, active: true },
    { name: 'Maria', age: 25, active: false },
    { name: 'Ivan', age: 35, active: true }
];

// map - element transformation
const names = users.map(user => user.name);

// filter - filtration
const activeUsers = users.filter(user => user.active);

// reduce - aggregation
const totalAge = users.reduce((sum, user) => sum + user.age, 0);

// find - search for the first element
const alex = users.find(user => user.name === 'Alexey');

// some - check the condition for at least one element
const hasActive = users.some(user => user.active);

// every - check the condition for all elements
const allActive = users.every(user => user.active);

// Method chains
const result = users
    .filter(user => user.active)
    .map(user => user.name)
    .sort();

Optional Chaining and Nullish Coalescing

These operators simplify working with potentially undefined values.

const user = {
    name: 'Alexey',
    address: {
        city: 'Moscow'
    }
};

// Optional chaining (?.) - secure access
const street = user.address?.street; // undefined, no error
const zip = user.address?.zip?.code; // undefined

// With methods
const result = user.getData?.(); // undefined if there is no method

// Nullish coalescing (??) - default value
const port = process.env.PORT ?? 3000;

// Difference from ||
const count = 0;
console.log(count || 10); // 10 (0 is considered falsy)
console.log(count ?? 10); // 0 (works only with null/undefined)

Conclusion

Modern JavaScript offers many tools for writing clean and efficient code. The features of ES6 and later versions do not just add new syntax, they change the approach to JavaScript programming, making it more expressive and reliable.

Start applying these features in your projects gradually. Over time, many of them will become a natural part of your coding style. Modern JavaScript opens up new horizons for developers, making the process of creating applications more productive and enjoyable.

Code is an educational platform for beginner developers. We create easy-to-understand courses and articles on Python, JavaScript, HTML, CSS, and other modern technologies.

Join our community!

Subscribe to our Kodik Telegram channel, where we publish:

  • New articles and tutorials on programming

  • Useful tips for developers

  • Analyzing complex topics in simple language

  • Current news from the IT world

Develop together with Kodik! 🚀

🎯Stop procrastinating

Liked the article?
Time to practice!

In Kodik, you don't just read — you write code immediately. Theory + practice = real skills.

Instant practice
🧠AI explains code
🏆Certificate

No registration • No card