ES6+ Features Every JavaScript Developer Should Know
Master modern JavaScript with ES6+ features including arrow functions, destructuring, modules, async/await, and more essential syntax improvements.
ES6+ Features Every JavaScript Developer Should Know
ECMAScript 2015 (ES6) and later versions introduced many powerful features that make JavaScript more expressive, readable, and maintainable. This guide covers the most important features you should master.
1. Arrow Functions #
Arrow functions provide a shorter syntax for writing functions and have different this
binding behavior.
Basic Syntax #
// Traditional function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
// With single parameter (parentheses optional)
const square = x => x * x;
// With no parameters
const greet = () => 'Hello World';
// With multiple statements
const processData = (data) => {
const cleaned = data.filter(item => item != null);
return cleaned.map(item => item.toUpperCase());
};
Lexical this
Binding #
// Traditional function - 'this' depends on how it's called
function Timer() {
this.seconds = 0;
// Problem: 'this' in setTimeout refers to window/global
setTimeout(function() {
this.seconds++; // 'this' is not the Timer instance
console.log(this.seconds); // undefined or error
}, 1000);
}
// Arrow function - 'this' is lexically bound
function Timer() {
this.seconds = 0;
// Arrow function inherits 'this' from enclosing scope
setTimeout(() => {
this.seconds++; // 'this' refers to Timer instance
console.log(this.seconds); // Works correctly
}, 1000);
}
// In class methods
class Counter {
constructor() {
this.count = 0;
}
increment() {
// Arrow function preserves 'this' binding
setTimeout(() => {
this.count++;
console.log(this.count);
}, 100);
}
}
2. Template Literals #
Template literals provide powerful string formatting with embedded expressions.
const name = 'Alice';
const age = 30;
// Old way
const message = 'Hello, my name is ' + name + ' and I am ' + age + ' years old.';
// Template literal
const message = `Hello, my name is ${name} and I am ${age} years old.`;
// Multi-line strings
const html = `
<div class="user-card">
<h2>${name}</h2>
<p>Age: ${age}</p>
<p>Status: ${age >= 18 ? 'Adult' : 'Minor'}</p>
</div>
`;
// Expression evaluation
const price = 19.99;
const tax = 0.08;
const total = `Total: $${(price * (1 + tax)).toFixed(2)}`;
// Function calls in templates
const formatDate = (date) => date.toLocaleDateString();
const today = new Date();
const dateString = `Today is ${formatDate(today)}`;
Tagged Template Literals #
// Create a custom template tag
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
const value = values[i] ? `<mark>${values[i]}</mark>` : '';
return result + string + value;
}, '');
}
const searchTerm = 'JavaScript';
const text = highlight`Looking for ${searchTerm} tutorials`;
// Result: "Looking for <mark>JavaScript</mark> tutorials"
3. Destructuring #
Destructuring allows you to extract values from arrays and objects into variables.
Array Destructuring #
const numbers = [1, 2, 3, 4, 5];
// Traditional way
const first = numbers[0];
const second = numbers[1];
// Destructuring
const [first, second] = numbers;
const [a, b, c] = numbers;
// Skip elements
const [first, , third] = numbers;
// Rest operator
const [head, ...tail] = numbers;
// head = 1, tail = [2, 3, 4, 5]
// Default values
const [x = 0, y = 0] = []; // x = 0, y = 0
const [p = 1, q = 2] = [10]; // p = 10, q = 2
// Swapping variables
let a = 1, b = 2;
[a, b] = [b, a]; // a = 2, b = 1
Object Destructuring #
const person = {
name: 'Alice',
age: 30,
email: '[email protected]',
address: {
city: 'New York',
country: 'USA'
}
};
// Traditional way
const name = person.name;
const age = person.age;
// Destructuring
const { name, age } = person;
// Rename variables
const { name: personName, age: personAge } = person;
// Default values
const { name, age, phone = 'N/A' } = person;
// Nested destructuring
const { address: { city, country } } = person;
// Rest operator
const { name, ...otherProps } = person;
// otherProps = { age: 30, email: '[email protected]', address: {...} }
Function Parameter Destructuring #
// Object parameter destructuring
function createUser({ name, age, email = 'no-email' }) {
console.log(`Creating user: ${name}, ${age}, ${email}`);
}
createUser({ name: 'Bob', age: 25 });
// Array parameter destructuring
function processCoordinates([x, y, z = 0]) {
console.log(`Processing: ${x}, ${y}, ${z}`);
}
processCoordinates([10, 20]);
// Complex destructuring with defaults
function apiCall({
url,
method = 'GET',
headers = {},
timeout = 5000
} = {}) {
console.log(`${method} ${url} (timeout: ${timeout}ms)`);
}
4. Spread and Rest Operators #
The spread (...
) operator expands arrays and objects, while rest collects multiple elements.
Spread Operator #
// Array spreading
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// Object spreading
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const combined = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 }
// Cloning arrays and objects
const originalArray = [1, 2, 3];
const clonedArray = [...originalArray];
const originalObject = { name: 'Alice', age: 30 };
const clonedObject = { ...originalObject };
// Function arguments
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
const result = sum(...numbers); // Same as sum(1, 2, 3)
// Converting NodeList to Array
const elements = document.querySelectorAll('div');
const elementsArray = [...elements];
Rest Operator #
// Function parameters
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4, 5); // 15
// Array destructuring
const [first, ...rest] = [1, 2, 3, 4, 5];
// first = 1, rest = [2, 3, 4, 5]
// Object destructuring
const { name, ...otherProps } = { name: 'Alice', age: 30, city: 'NYC' };
// name = 'Alice', otherProps = { age: 30, city: 'NYC' }
5. Enhanced Object Literals #
ES6 provides shorthand syntax for creating objects.
const name = 'Alice';
const age = 30;
const city = 'New York';
// Traditional object literal
const person = {
name: name,
age: age,
city: city,
greet: function() {
return 'Hello, ' + this.name;
}
};
// Enhanced object literal
const person = {
name, // Shorthand property
age,
city,
greet() { // Method shorthand
return `Hello, ${this.name}`;
},
// Computed property names
[getPropertyName()]: 'dynamic value',
[`prefix_${Date.now()}`]: 'timestamp property'
};
function getPropertyName() {
return 'dynamicProp';
}
6. Default Parameters #
Functions can have default parameter values.
// Traditional approach
function greet(name, greeting) {
name = name || 'World';
greeting = greeting || 'Hello';
return greeting + ', ' + name;
}
// Default parameters
function greet(name = 'World', greeting = 'Hello') {
return `${greeting}, ${name}`;
}
// Complex default values
function createUser(name, options = {}) {
const {
age = 18,
role = 'user',
active = true
} = options;
return { name, age, role, active };
}
// Function as default value
function log(message, timestamp = new Date()) {
console.log(`[${timestamp.toISOString()}] ${message}`);
}
// Default parameters with destructuring
function apiRequest({
url,
method = 'GET',
headers = {},
timeout = 5000
} = {}) {
console.log(`${method} ${url}`);
}
7. Classes #
ES6 introduces class syntax for object-oriented programming.
// Class declaration
class Person {
// Constructor
constructor(name, age) {
this.name = name;
this.age = age;
}
// Method
greet() {
return `Hello, I'm ${this.name}`;
}
// Getter
get info() {
return `${this.name} (${this.age} years old)`;
}
// Setter
set age(value) {
if (value < 0) {
throw new Error('Age cannot be negative');
}
this._age = value;
}
get age() {
return this._age;
}
// Static method
static compare(person1, person2) {
return person1.age - person2.age;
}
}
// Inheritance
class Employee extends Person {
constructor(name, age, jobTitle) {
super(name, age); // Call parent constructor
this.jobTitle = jobTitle;
}
greet() {
return `${super.greet()}, I work as a ${this.jobTitle}`;
}
work() {
return `${this.name} is working`;
}
}
// Usage
const person = new Person('Alice', 30);
const employee = new Employee('Bob', 25, 'Developer');
console.log(person.greet()); // "Hello, I'm Alice"
console.log(employee.greet()); // "Hello, I'm Bob, I work as a Developer"
console.log(Person.compare(person, employee)); // 5
8. Modules (Import/Export) #
ES6 modules provide a standardized way to organize and share code.
Named Exports #
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// Alternative syntax
const subtract = (a, b) => a - b;
const divide = (a, b) => a / b;
export { subtract, divide };
Default Exports #
// calculator.js
class Calculator {
add(a, b) { return a + b; }
subtract(a, b) { return a - b; }
}
export default Calculator;
// Or inline
export default class Calculator {
add(a, b) { return a + b; }
subtract(a, b) { return a - b; }
}
Importing #
// Named imports
import { add, multiply, PI } from './math.js';
// Rename imports
import { add as sum, multiply as product } from './math.js';
// Import all
import * as math from './math.js';
console.log(math.add(2, 3));
// Default import
import Calculator from './calculator.js';
// Mixed imports
import Calculator, { add, multiply } from './calculator.js';
// Dynamic imports
async function loadModule() {
const module = await import('./math.js');
console.log(module.add(2, 3));
}
9. Promises and Async/Await #
Modern asynchronous programming with promises and async/await.
// Promise creation
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve({ data: 'Hello World' });
} else {
reject(new Error('Failed to fetch data'));
}
}, 1000);
});
};
// Promise consumption
fetchData()
.then(result => console.log(result))
.catch(error => console.error(error));
// Async/await
async function getData() {
try {
const result = await fetchData();
console.log(result);
} catch (error) {
console.error(error);
}
}
// Multiple async operations
async function fetchMultipleData() {
try {
// Sequential
const data1 = await fetchData();
const data2 = await fetchData();
// Parallel
const [result1, result2] = await Promise.all([
fetchData(),
fetchData()
]);
return { result1, result2 };
} catch (error) {
console.error('Error fetching data:', error);
}
}
10. Enhanced Array Methods #
New array methods for functional programming.
const numbers = [1, 2, 3, 4, 5];
// find() - returns first element that matches
const firstEven = numbers.find(n => n % 2 === 0); // 2
// findIndex() - returns index of first match
const firstEvenIndex = numbers.findIndex(n => n % 2 === 0); // 1
// includes() - checks if array contains element
const hasThree = numbers.includes(3); // true
// Array.from() - creates array from iterable
const chars = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
// Array.of() - creates array from arguments
const arr = Array.of(1, 2, 3); // [1, 2, 3]
// fill() - fills array with value
const filled = new Array(5).fill(0); // [0, 0, 0, 0, 0]
// keys(), values(), entries()
const fruits = ['apple', 'banana', 'orange'];
for (const index of fruits.keys()) {
console.log(index); // 0, 1, 2
}
for (const value of fruits.values()) {
console.log(value); // 'apple', 'banana', 'orange'
}
for (const [index, value] of fruits.entries()) {
console.log(index, value); // 0 'apple', 1 'banana', 2 'orange'
}
11. Map and Set #
New collection types for better data management.
Map #
// Map creation
const userMap = new Map();
// Setting values
userMap.set('alice', { name: 'Alice', age: 30 });
userMap.set('bob', { name: 'Bob', age: 25 });
// Getting values
const alice = userMap.get('alice');
// Checking existence
if (userMap.has('alice')) {
console.log('Alice exists');
}
// Iterating
for (const [key, value] of userMap) {
console.log(key, value);
}
// Map with any key type
const objMap = new Map();
const keyObj = { id: 1 };
objMap.set(keyObj, 'value for object key');
objMap.set('string', 'value for string key');
objMap.set(42, 'value for number key');
// Map methods
console.log(userMap.size); // 2
userMap.delete('alice');
userMap.clear();
Set #
// Set creation
const uniqueNumbers = new Set();
// Adding values
uniqueNumbers.add(1);
uniqueNumbers.add(2);
uniqueNumbers.add(2); // Duplicate ignored
// Converting array to set (removes duplicates)
const numbers = [1, 2, 2, 3, 3, 4];
const uniqueSet = new Set(numbers);
const uniqueArray = [...uniqueSet]; // [1, 2, 3, 4]
// Set methods
console.log(uniqueNumbers.has(1)); // true
console.log(uniqueNumbers.size); // 2
// Iterating
for (const value of uniqueNumbers) {
console.log(value);
}
// Set operations
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);
// Union
const union = new Set([...setA, ...setB]); // {1, 2, 3, 4, 5}
// Intersection
const intersection = new Set([...setA].filter(x => setB.has(x))); // {3}
// Difference
const difference = new Set([...setA].filter(x => !setB.has(x))); // {1, 2}
12. Symbols and Iterators #
Symbols #
// Creating symbols
const sym1 = Symbol();
const sym2 = Symbol('description');
const sym3 = Symbol('description');
console.log(sym2 === sym3); // false (symbols are unique)
// Using symbols as object keys
const obj = {
[sym1]: 'value1',
[sym2]: 'value2'
};
// Symbols don't appear in normal iteration
console.log(Object.keys(obj)); // []
console.log(Object.getOwnPropertySymbols(obj)); // [sym1, sym2]
// Well-known symbols
const iterableObj = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { done: true };
}
}
};
}
};
for (const value of iterableObj) {
console.log(value); // 1, 2, 3
}
13. Proxy and Reflect #
Advanced metaprogramming features.
Proxy #
// Object proxy
const user = {
name: 'Alice',
age: 30
};
const userProxy = new Proxy(user, {
get(target, property) {
console.log(`Getting ${property}`);
return target[property];
},
set(target, property, value) {
console.log(`Setting ${property} to ${value}`);
if (property === 'age' && value < 0) {
throw new Error('Age cannot be negative');
}
target[property] = value;
return true;
},
has(target, property) {
console.log(`Checking if ${property} exists`);
return property in target;
}
});
// Usage
console.log(userProxy.name); // Logs: "Getting name", then "Alice"
userProxy.age = 25; // Logs: "Setting age to 25"
console.log('name' in userProxy); // Logs: "Checking if name exists", then true
Reflect #
// Reflect provides methods for interceptable operations
const obj = { name: 'Alice', age: 30 };
// Reflect.get()
const name = Reflect.get(obj, 'name'); // 'Alice'
// Reflect.set()
Reflect.set(obj, 'city', 'New York');
// Reflect.has()
const hasAge = Reflect.has(obj, 'age'); // true
// Reflect.deleteProperty()
Reflect.deleteProperty(obj, 'age');
// Reflect.ownKeys()
const keys = Reflect.ownKeys(obj); // ['name', 'city']
Practical Examples #
1. Modern API Client #
class ApiClient {
constructor(baseURL) {
this.baseURL = baseURL;
}
async request(endpoint, { method = 'GET', body, headers = {} } = {}) {
const url = `${this.baseURL}${endpoint}`;
const config = {
method,
headers: {
'Content-Type': 'application/json',
...headers
}
};
if (body) {
config.body = JSON.stringify(body);
}
try {
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('API request failed:', error);
throw error;
}
}
// Method shortcuts
get(endpoint, options) {
return this.request(endpoint, { ...options, method: 'GET' });
}
post(endpoint, data, options) {
return this.request(endpoint, { ...options, method: 'POST', body: data });
}
put(endpoint, data, options) {
return this.request(endpoint, { ...options, method: 'PUT', body: data });
}
delete(endpoint, options) {
return this.request(endpoint, { ...options, method: 'DELETE' });
}
}
// Usage
const api = new ApiClient('https://api.example.com');
async function fetchUserData() {
try {
const users = await api.get('/users');
const newUser = await api.post('/users', { name: 'Alice', email: '[email protected]' });
return { users, newUser };
} catch (error) {
console.error('Error fetching user data:', error);
}
}
2. Event Emitter Class #
class EventEmitter {
constructor() {
this.events = new Map();
}
on(event, callback) {
if (!this.events.has(event)) {
this.events.set(event, new Set());
}
this.events.get(event).add(callback);
}
off(event, callback) {
if (this.events.has(event)) {
this.events.get(event).delete(callback);
}
}
emit(event, ...args) {
if (this.events.has(event)) {
for (const callback of this.events.get(event)) {
callback(...args);
}
}
}
once(event, callback) {
const onceCallback = (...args) => {
callback(...args);
this.off(event, onceCallback);
};
this.on(event, onceCallback);
}
}
// Usage
const emitter = new EventEmitter();
emitter.on('user-login', (user) => {
console.log(`User ${user.name} logged in`);
});
emitter.once('app-ready', () => {
console.log('App is ready!');
});
emitter.emit('user-login', { name: 'Alice' });
emitter.emit('app-ready');
Browser Compatibility #
Most ES6+ features are supported in modern browsers, but consider:
- Use Babel for transpilation if you need to support older browsers
- Check caniuse.com for specific feature compatibility
- Use polyfills for missing features
- Consider TypeScript for better development experience
Summary #
ES6+ features that every JavaScript developer should know:
- Arrow Functions - Concise syntax and lexical
this
- Template Literals - String interpolation and multi-line strings
- Destructuring - Extract values from arrays and objects
- Spread/Rest - Expand and collect elements
- Classes - Object-oriented programming syntax
- Modules - Import/export for code organization
- Promises/Async-Await - Modern asynchronous programming
- Enhanced Arrays - New methods for functional programming
- Map/Set - Better collection types
- Default Parameters - Function parameter defaults
These features make JavaScript more powerful, readable, and maintainable. Start using them in your projects today!
Next Steps #
Related Tutorials
Asynchronous JavaScript - Promises, Async/Await, and Fetch
Master asynchronous programming in JavaScript with promises, async/await, and the Fetch API. Learn to handle API calls, timers, and concurrent operations.
Best Way to Learn JavaScript: Common Questions Answered
Find answers to frequently asked questions about the best way to learn JavaScript, including timelines, resources, and effective study methods.
Last updated: Jan 11, 2025
DOM Manipulation with JavaScript - Complete Guide
Master DOM manipulation in JavaScript. Learn to select elements, modify content, handle events, and create dynamic web pages with practical examples.
How to Turn On JavaScript: Complete Guide for All Browsers
Learn how to turn on JavaScript in Chrome, Firefox, Safari, and Edge with step-by-step instructions and troubleshooting tips.
Last updated: Jul 11, 2025