JavaScript ES6 Features
The ES6 features modern JavaScript is built on — let/const, arrow functions, destructuring, template literals, modules, Promises and classes.
Last reviewed
Recommended
JavaScript ES6 Features — Timed Test (3 questions)
No account needed. Answers and explanations arrive when you submit.
What this topic tests
The mix every JavaScript ES6 Features set is built to, and the questions published against it so far. Nothing here is hidden before you start.
| Level | Target share | Published |
|---|---|---|
| Easy | 40% | 0 |
| Medium | 40% | 3 |
| Hard | 20% | 0 |
| Total | 3 |
JavaScript ES6 Features — the theory
ES6 (also called ECMAScript 2015) was a major update to the JavaScript language that introduced a range of features still central to how modern JavaScript is written today.
`let` and `const`. ES6 introduced let and const as alternatives to the older var keyword for declaring variables. let declares a variable that can be reassigned, while const declares a variable that cannot be reassigned after its initial value is set. Both have block scope (limited to the enclosing block of code, like inside an if statement or loop), unlike var, which has function scope — a difference that eliminates a common category of bugs from the earlier syntax.
Arrow functions. Arrow functions provide a more concise syntax for writing functions, using => instead of the function keyword, like (x) => x * 2. Beyond brevity, arrow functions also handle the this keyword differently than traditional functions — they inherit this from their surrounding context rather than defining their own, which resolves a common source of confusion in earlier JavaScript when using this inside callbacks.
Template literals. Template literals, written with backticks instead of quotes, allow embedding expressions directly inside a string using ${expression} syntax, and support multi-line strings without special escape characters. This is generally more readable than concatenating strings with the + operator, especially when combining multiple variables into a single string.
Destructuring. Destructuring allows extracting values from arrays or objects into individual variables in a single, concise statement, rather than accessing each value separately by index or key. This is widely used for extracting specific properties from an object or specific items from an array in a way that's both shorter and clearer about intent than repeated individual accesses.
Default parameters. ES6 allows function parameters to have default values specified directly in the function definition, used automatically when a caller doesn't provide a value for that parameter — replacing older, more verbose patterns for handling optional parameters.
Modules. ES6 introduced a standardized module system using import and export statements, allowing code to be organized across multiple files with explicit, clear dependencies between them — replacing a variety of inconsistent, non-standard module patterns that existed before.
Promises. ES6 introduced Promises as a standardized way of handling asynchronous operations, representing a value that may not be available yet but will be at some point (or will fail with an error) — a significant improvement over earlier callback-based patterns for asynchronous code, and the direct foundation for the async/await syntax covered in more depth elsewhere.
Classes. ES6 introduced class syntax for defining objects and inheritance in a way that's more familiar to developers coming from other object-oriented languages, though under the hood it's built on JavaScript's existing prototype-based inheritance model rather than replacing it entirely.
Spread and rest. ES6 also introduced the three-dot ... syntax, which plays two complementary roles. As the spread operator, it expands an array or object into its individual elements — copying an array with [...items], merging objects, or passing an array's elements as separate function arguments. As rest syntax, it does the reverse, collecting remaining values into an array — as in a function that accepts any number of arguments with (...args), or destructuring that captures "everything else." Together they replaced a family of clumsy older patterns and are now everywhere in modern code.
Shorthand object syntax. ES6 made object literals lighter: when a property name matches the variable holding its value, { name } works in place of { name: name }; methods can be written without the function keyword; and property names can be computed from expressions inside square brackets. Small conveniences individually, but they account for much of the visual difference between pre- and post-ES6 code.
`for...of` and iterables. ES6 standardized a protocol for iteration and a matching loop: for...of walks directly over the values of any iterable — arrays, strings, Maps, Sets — without index bookkeeping, and it is the natural companion to the new collection types ES6 added: Map, which holds key-value pairs with keys of any type, and Set, which holds unique values and makes deduplication and membership checks straightforward.
Reading older code. Because so much JavaScript predates ES6, recognizing the older equivalents remains useful: var where let/const now belong, function expressions where arrows would be used, string concatenation where template literals would serve, and callback pyramids where Promises now stand. Being able to read both styles — and to modernize the old one confidently — is a routine part of working in long-lived JavaScript codebases, where files from different eras sit side by side.
Why ES6 matters today. Even though JavaScript has continued to evolve with newer yearly updates since ES6, the ES6 feature set remains foundational to how modern JavaScript is written and taught — most contemporary JavaScript code, tutorials, and frameworks assume familiarity with let/const, arrow functions, destructuring, and modules as baseline knowledge rather than advanced or optional features.
Sample questions
Three questions from this topic, with the answer and the reasoning shown.
Q1MediumHow do arrow functions differ from traditional functions regarding `this`?
- Arrow functions inherit `this` from their surrounding context rather than defining their ownCorrect
- Arrow functions cannot access `this` under any circumstances
- Traditional functions always inherit `this` from arrow functions
- There is no difference in how `this` behaves
Explanation
A traditional function decides what this means at call time, from how it was called. An arrow function does not decide at all. MDN is blunt about it: "Arrow functions don't have their own bindings to this, arguments, or super, and should not be used as methods." Having no binding of its own, the name this inside an arrow resolves outward into the enclosing scope, exactly like any other closed-over variable. That is why "arrow functions establish this based on the scope the arrow function is defined within, and the this value does not change based on how the function is invoked" — call, apply and bind cannot move it afterwards. The bug this prevents is the one waiting in every callback: hand a traditional function to addEventListener or setTimeout and this becomes the element or undefined rather than your object, which is why the old code around it is full of bind calls and a self variable. The same property is a trap in the other direction. Write an object method as an arrow and this looks straight past the object at whatever surrounded the literal.
Q2MediumWhat did ES6 introduce for handling asynchronous operations?
- PromisesCorrect
- The var keyword
- HTML template tags
- CSS grid layout
Explanation
Promises are the ES6 answer to a specific pain. Before them every asynchronous step took a callback, and stacking steps meant nesting callbacks inside callbacks; MDN names the result exactly as you would expect, noting that "In the old days, doing several asynchronous operations in a row would lead to the classic callback hell". A promise replaces that nesting with an ordinary value you can hold, return and chain, because it "represents the eventual completion (or failure) of an asynchronous operation and its resulting value". The sixth edition's own introduction lists "lexical block scoping, iterators and generators, promises for asynchronous programming, destructuring patterns" among its major enhancements, which is where the ES6 label in this question comes from. You will collide with this tomorrow the first time you call fetch: it hands back a promise, so you either chain then and catch onto it or put await in front of it. Async and await came later and are syntax layered over this same object, so promises are not groundwork you get to skip. Each wrong answer names something real that has nothing to do with asynchrony.
Q3MediumWhat is a key difference between `let` and `var` in JavaScript?
- `let` has block scope, while `var` has function scopeCorrect
- `var` was introduced in ES6 and `let` was not
- `let` cannot hold numeric values
- There is no functional difference between them
Explanation
The difference is about which curly braces count. MDN states that "let declarations are scoped to blocks as well as functions", while for the older keyword "other block constructs, including block statements, try...catch, switch, headers of one of the for statements, do not create scopes for var, and variables declared with var inside such a block can continue to be referenced outside the block". A var written inside an if body or a for header therefore leaks into the whole surrounding function. Two consequences follow that you will actually meet. First, the classic loop bug: a for loop declared with var shares one binding across every iteration, so callbacks created inside it all read the final value, while let creates a fresh binding per iteration and each callback keeps its own. Second, "let declarations can only be accessed after the place of declaration is reached (see temporal dead zone)", so touching one too early throws a ReferenceError instead of quietly handing you undefined the way a hoisted var does. That noisy failure is the feature — it converts a silent wrong value into an error you can find.
Practise all 3 questions
Every published question in JavaScript ES6 Features, with its answer and explanation.