ES6 (ECMAScript 2015) and later versions introduced the syntax that defines modern JavaScript: arrow functions, template literals, destructuring, and the spread/rest operators. This lesson walks through each feature with practical examples so you can write cleaner, more expressive code.
1Modern JS (ES6+) | JavaScript Tutorial - In-Depth Guide Part 1
Arrow functions (() => {}) are ES6's shorthand syntax for writing functions. Beyond being shorter to type, they don't create their own this binding ā they inherit this from the surrounding scope, which is why they're the default choice for callbacks in modern code.
const greet = () => 'Hello World!';Modern ES6+
2Modern JS (ES6+) | JavaScript Tutorial - In-Depth Guide Part 2
When an arrow function's body is a single expression written without curly braces, that expression's value is returned automatically ā no return keyword needed. This is called an implicit return, and it's why (a, b) => a + b works as a complete one-line function.
const add = (a, b) => a + b;
console.log(add(5, 5)); // 10Implicit Return
3Modern JS (ES6+) | JavaScript Tutorial - In-Depth Guide Part 3
Template literals, delimited by backticks instead of quotes, let you embed variables and expressions directly inside a string using ${} syntax. This replaces the older, more error-prone pattern of concatenating strings together with the + operator.
const user = 'Alex';
const msg = `Hello, ${user}!`;Template Literals
4Modern JS (ES6+) | JavaScript Tutorial - In-Depth Guide Part 4
Object destructuring unpacks specific properties directly into standalone variables in a single line, using { } on the left side of an assignment ā instead of writing out person.name and person.age separately.
const person = { name: 'Alex', age: 25 };
const { name, age } = person;Destructuring
5Modern JS (ES6+) | JavaScript Tutorial - In-Depth Guide Part 5
During destructuring, you can rename a property to a different local variable name using a colon ā { name: userName } pulls the name property out but stores it in a variable called userName. This is useful for avoiding naming collisions with existing variables.
const { name: userName } = person;
console.log(userName); // 'Alex'Aliasing
6Modern JS (ES6+) | JavaScript Tutorial - In-Depth Guide Part 6
The spread operator (...) expands an array's elements in place. [...original, 4] creates a brand-new array containing every item from original plus a new one at the end, without mutating original itself ā essential for writing immutable update logic.
const original = [1, 2, 3];
const copy = [...original, 4];Spread Operator
7Modern JS (ES6+) | JavaScript Tutorial - In-Depth Guide Part 7
The rest operator reuses the same ... syntax as spread but works in the opposite direction: placed in a function's parameter list, ...numbers gathers any number of passed-in arguments into one real array you can call .reduce() or .map() on.
function sum(...numbers) {
return numbers.reduce((a, b) => a + b);
}Rest Operator
8Modern JS (ES6+) | JavaScript Tutorial - In-Depth Guide Part 8
With arrow functions, implicit returns, template literals, destructuring, and spread/rest under your belt, you now have the core modern syntax that shows up throughout real-world JavaScript codebases and frameworks like React.
<h1>Modern JS Master Unlocked!</h1>ES6 Mastered
9Step-by-Step Breakdown
Arrow functions are a shorter way to write functions in JavaScript, introduced in ES6. Instead of the 'function' keyword, you use a set of parentheses and an arrow.
When an arrow function's body is a single expression, you can drop the curly braces and the 'return' keyword entirely ā the expression's value is returned automatically. This is called an implicit return.
Checkpoint: What is the correct syntax for an Arrow Function with no parameters?
- āfunction() { ... }
- ā() => { ... }
Template literals use backticks instead of quotes, letting you embed variables directly inside a string with the dollar-curly-brace syntax, instead of chaining pieces together with the plus operator.
Destructuring lets you unpack properties from an object straight into standalone variables in one line, instead of accessing each property individually with dot notation.
You can also rename a property while destructuring it, using a colon to map the original property name to a new local variable name ā useful for avoiding naming collisions.
Checkpoint: What symbol is used to destructure an OBJECT?
- ā[ ] (Square Brackets)
- ā{ } (Curly Braces)
The spread operator, three dots followed by an array, expands that array's elements in place ā this is how you create a new array containing all of another array's items plus extras, without mutating the original.
The rest operator uses the same three-dot syntax as spread, but in the opposite direction: inside a function's parameter list, it gathers any number of passed-in arguments into a single real array.
You now know the core ES6+ syntax used throughout modern JavaScript: arrow functions, implicit returns, template literals, destructuring, and the spread and rest operators.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Use Template Literals to Build Dynamic ARIA Labels Cleanly
Accessible components often need dynamic aria-label or aria-describedby text (e.g. 'Remove item 3 of 8'). Template literals make this readable in one line instead of concatenating several string pieces, reducing the chance of a malformed label string.
el.setAttribute('aria-label', `Remove item ${index + 1} of ${total}`);SEO Implications
- 1
Unsupported ES6+ Syntax Can Break Rendering for Older Crawlers or Browsers
If ES6+ syntax like optional chaining or destructuring ships without transpilation for environments that don't support it, a SyntaxError can halt the entire script ā including any client-side rendering search engines rely on to see your content. Transpiling with a tool like Babel and testing your build's actual browser support avoids this.
Best Practices
Use Destructuring for Function Parameters When Passing an Options Object
Instead of `function createUser(options) { const name = options.name; ... }`, destructure directly in the parameter list: `function createUser({ name, age }) {}` ā it's shorter, documents which properties the function actually uses, and avoids repetitive options.x access throughout the function body.
Prefer the Spread Operator Over Array.prototype.concat() or Object.assign()
[...arr1, ...arr2] and { ...obj1, ...obj2 } are more readable and consistent than mixing concat(), slice(), and Object.assign() calls, and they make it visually obvious at a glance that a new array or object is being created rather than an existing one being mutated.
Frequent Bugs
Copying an array or object with const copy = original produces a second reference to the same data instead of a real copy.
Assignment for arrays and objects copies the reference, not the data ā mutating 'copy' also mutates 'original' because they point to the same thing in memory. Use the spread operator, `const copy = [...original]` or `const copy = { ...original }`, to create an actual shallow copy.
Real-World Examples
Merging Default Options with User-Provided Overrides
A configurable widget needed to accept partial configuration from the caller while still falling back to sensible defaults for any options the caller didn't specify.
const defaultOptions = { theme: 'light', animate: true, duration: 300 };
function createWidget(userOptions = {}) {
const options = { ...defaultOptions, ...userOptions };
return options;
}
createWidget({ theme: 'dark' });
// { theme: 'dark', animate: true, duration: 300 }