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 reassignedUse const by default for all variables that do not require reassignment. This makes the code more predictable and protects against accidental changes.
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); // 10Template 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 barksArray 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! 🚀
