ECMAScript 6 Tutorial 2025

Written by
Sam LaRiviere and Alex Stewart
15 mins
Mar 31, 2025
Subscribe for more insights
Thank you for your interest in the syllabus.

We'll be in touch soon with more information!

In the meantime, if you have any questions, don’t hesitate to reach out to our team.

Oops! Something went wrong while submitting the form.

TLDR: What is ECMAScript

ES6 JavaScript introduced some of the most groundbreaking features that engineers use in their code every day. From the introduction of the const and let keywords, to spread and rest syntax to ES6 modules, and everything in between, this powerful update has revolutionized the way in which engineers write clean, efficient, and maintainable code. We encourage you to try them out for yourself and check out the Codesmith blog for articles that dive deeper into these ES6 features.

We at Codesmith cultivate technologists who are at the intersection of society and tech, able to meet this moment and thrive in an ever-changing world. Ready to become a modern software engineer?

What is ECMAScript?

ECMAScript is a standard that defines scripting languages like JavaScript. Managed by ECMA International, it serves as the blueprint for various scripting languages such as JScript and ActionScript, but JavaScript is the most widely adopted implementation of ECMAScript.

ECMAScript releases periodic updates to make scripting languages more efficient and user-friendly. Let’s dive into JavaScript ES6 (also known as ECMAScript 2015), one of the most important versions, and explore the key features it introduced.

What is JavaScript ES6?

JavaScript ES6, or ECMAScript 2015, was a major update that revolutionized JavaScript by improving readability and efficiency. Before ES6, JavaScript was less effective in handling large-scale applications. ES6 introduced new syntax and features that made JavaScript cleaner and more powerful.

Key Features of JavaScript ES6:

  1. Let and Const for Variable Declarations
  2. Arrow Functions
  3. Template Literals
  4. Destructuring
  5. Spread and Rest Operators
  6. Classes
  7. Promises
  8. Default Parameters
  9. For...of Loop
  10. Set and Map Objects
  11. ES6 Modules

The let keyword in ES6 JavaScript

In ES6, let and const replace var for variable declarations. Let provides block scoping, which means variables declared with let are only accessible within the code block they are defined. Var, on the other hand, is function-scoped and can cause issues with accidental variable reuse.

Take a look below to see an example.

Below, let’s block scope allows us to declare the variable codeSmith which will only be accessible within the code block between lines 22-25 and is not tied to the codeSmith variable declared on line 20.

If we were to execute the code above using var instead of let, our code would reassign the value of codeSmith on line 23.

Similar to var, variables declared using let are mutable. This means that if you initially assign a value to a let variable, you can reassign (change) that value later on with no issue.

Let’s say you have a function that keeps track of log in attempts. You initialize a variable let loginAttempts = 0. Each time a user tries to log in unsuccessfully, you want to increment loginAttempts so that, once it hits 3, the user needs to wait one hour before attempting to log in again. 

Often, we want our variables’ values to be immutable, meaning that they cannot be reassigned. This is where the const keyword comes in.

The const keyword

Const ensures a variable cannot be reassigned after initialization, although objects and arrays can still be mutated. This is particularly useful for ensuring immutability within your code.

Just like let, the const keyword is block scoped. Below, a variable is declared using const on line 20. Within the code block between lines 23 and 26, a variable with the same label of codeSmith is declared but only accessible within that code block. 

It’s important to note that, while variables declared with const cannot be reassigned, their values can still be mutated if they reference an object (including arrays). For example, suppose Jane owns a house (represented by an object). She can repaint the walls or renovate rooms (mutating the object’s properties), but she cannot sell her current house and buy a completely new one (reassigning the variable to a different object).

Below, the value of the first element  “array” can be reassigned as arrays are mutable. However, trying to reassign “array” to a completely new array (or any other value) will throw an error.

Arrow Functions in JavaScript ES6

Arrow functions simplify the syntax for writing function expressions. They are especially useful for small functions and improve code readability. The syntax is shorter compared to traditional function expressions:

A function declaration is when a function is defined without being assigned to a variable.

A function expression is when a function is assigned to a variable.

In JavaScript ES6, arrow functions allow for shorter, and oftentimes, more readable syntax for writing function expressions

In the example above:

  •  A function expression is initialized using var.
  •  Using ES6, that expression can (and should) be initialized using const. This ensures that secretNumber cannot accidentally be reassigned to anything else.
  •  Using arrow function syntax, secretNumber is defined using shorter syntax
  •  The return keyword can be omitted if you are writing a function as a single line statement as seen on line 48.

Arrow functions are good to use when you have to write a simple function like the one in the example above and do not necessarily need a function body. They are also useful within higher order functions (map, filter, reduce, etc.). 

Arrow functions should not be used when you need your function to be bound to its own this object or when defining constructor functions. Check out this article to learn more about JavaScript Arrow functions.

ES6 template literals

Template literals make string interpolation and multi-line strings much easier than traditional string concatenation.

Template literals are an incredibly useful feature introduced by JavaScript ES6 and you’re going to find yourself using them quite a lot throughout your career. Before their introduction in 2015, strings had to be concatenated in the following way:

The example above works fine, but if you’re concatenating long strings, this can take quite a bit of time and become pretty tedious. This is remedied using a template literal. In the example below: 

  • Backticks must be used instead of single or double quotes.
  • The parameter to be added into the string must be placed within a dollar sign and curly brackets ( ${name} ).

Template literals can also be used for expression embedding as seen below.

It’s important to also note that template literals preserve line breaks allowing you to create strings that span multiple lines without having to use ‘/n’. 

Destructuring in ES6 JavaScript

Destructuring allows for easy extraction of values from objects or arrays and assigning them to variables in a cleaner, more readable way.

If you’re new to ES6, destructuring may feel a bit difficult to wrap your head around because the syntax is different from traditional variable assignments but, with a little practice, it will start to feel intuitive. Destructuring allows you to access properties within an object and assign them to a variable at the same time. 

Multiple properties can be accessed within the object at the same time. Note that the order of the keys does not matter, but the variable being initialized must have the same name as the corresponding property key in the object (exObject). For example, below, 

const { doggies } = exObj would log ‘undefined’

Destructuring can be used for arrays as well. In the example below, the variable dog will have the value of the element with the matching index in exArray (0 in this case).

Similar to object destructuring, multiple elements can be accessed at the same time.

You can also skip elements in the array by leaving gaps.

For a more detailed breakdown of destructuring in JavaScript, check out this article.

ES6 spread operator

The spread operator is used to take the elements of an array or object and spread them out into individual items.

Below, we see an example of the spread operator being used to spread the properties in obj to be combined with newObj resulting in an object with four properties.

This JavaScript ES6 feature is also an excellent way to concatenate two arrays. Below, the spread operator spreads the elements within array out into individual elements within newArray resulting in one large array with all five elements.

In the example above, the spread operator creates a shallow copy of exArray and pastes the elements into newArray. Below, we see what would happen if we did not use the spread operator.

  • exArray is defined and assigned to an array containing strings ‘hello’, ‘hi’, and ‘bye’.
  • newArray1 creates a new array containing a reference to exArray.
  • newArray2 creates a new array containing a shallow copy of the elements within exArray.
  • Compare the console.log’s of newArray1 and newArray2  on lines 78 and 79
  • On line 81, the first element in exArray is reassigned to ‘hotdog’
  • Because newArray1 contains a reference to exArray this change is reflected on line 82
  • This change is not reflected on line 83 because the spread operator created a shallow copy of exArray when newArray2 was defined on line 77.

If you're still unsure about these concepts, don't worry! With more experience, you'll get comfortable with the difference between primitive types (like strings and numbers, which are copied by value) and composite types (like arrays and objects, which are copied by reference). Understanding how value and reference assignments work will make these ideas clearer over time but, for our purposes today, just know that the spread operator allows you to take the elements/properties of an array/object and spread them out into individual items.

ES6 JavaScript rest parameter 

It can be difficult to differentiate between the rest parameter and spread operator as they use the exact same syntax (...). You can think of the rest parameter as doing the opposite of the spread operator. While the spread operator will spread the elements of an object or array into individual items, the rest parameter will take multiple individual elements and combine them into an array. In the example below, num1 will pair with 1, num 2 will pair with 2 and nums will pair with the rest of the inputs.

This JavaScript ES6 feature is particularly useful when defining a function that may take an unknown number of arguments. The rest parameter creates an iterable array containing all of the remaining arguments after the named arguments have been assigned. Below is a simple example of how an unknown number of inputs can be operated on within a function using the rest parameter to transform those inputs into an array.

For more information on the spread operator and rest parameters, be sure to check out Codesmith’s blog on the subjects.

ES6 JavaScript classes

Another major feature added to JavaScript by ES6 is classes. Before ES6, inheritance was handled by manually manipulating objects’ prototypal chains to share methods and properties. For example:

Above, each new instance of the Animal constructor function will have its own unique species property (seen on line 124). Because the speak method has been placed in the Animal prototype, each new instance of Animal (dog and monkey) will have access to it as we see on lines 126 and 127.

This syntax can take a while to understand and is considered to be a bit antiquated today. Below we see an example of what is often referred to as “syntactic sugar”. Everything is still happening the same under the hood, but ES6 classes provide a more concise syntax for implementing inheritance.

In the example above:

  •  A class of Animal initialized on line 113. 
  • This class has a constructor function that takes in a type.
  • Each new instance of Animal will have its own unique species property.
  • On line 120, we see the ES6 syntax for adding a method (speak) to a class’s prototype as opposed to manually adding it (ie: Animal.prototype.speak = function(){...}).

With ES6 classes, unique data (like this.species) goes directly on the constructor while the functionality that we want each instance to have access to (like the speak method) belongs on the prototype.

Prototypal inheritance and ES6 classes are incredibly powerful. It can take a little bit of time to get the hang of it but, luckily, Codesmith offers some fantastic resources on the subjects. Be sure to take a look at their “Hard Parts” lecture and CSX unit on “Prototypal Inheritance and ES6 Classes”.

ES6 JavaScript promises

JavaScript’s ES6 Promises were introduced to provide a simple, uniformed way of handling asynchronous code. Before ES6, asynchronous operations were primarily handled using callbacks and third party libraries. 

With the Promise Object, engineers have complete agency over the asynchronous code they want to write in a standardized, straight forward syntax. This has improved readability, allowed for the chaining of asynchronous functions, and made error handling a lot easier.

A Promise is an object that represents a value that may not be available right away but will be resolved eventually. The Promise Object:

  • Has one of three states, Pending, Fulfilled, or Rejected. 
  • Is created using the Promise Constructor with the new keyword (sound familiar?). 
  • The Promise Constructor will take in a function (known as the executor). 
  • The executor takes two parameters, resolve and reject.

In the example above:

  • promiseEx is will either resolve or reject after 1000 milliseconds (1 second)
  • Within our executor we use setTimeout to simulate any asynchronous operation that may take time to either resolve or reject. This setTimeout function could be replaced with something like a fetch request to an API, reading or writing files, parsing JSON or any other asynchronous functionality you can think of. In this case, it’s just waiting one second to check if a random number between 0 and 10 is even. If it’s even, it will invoke resolve, if it’s odd, it will invoke reject.
  • if promiseEx resolves we use .then() to handle whatever we pass into resolve() (‘truthy answer!’ in this case) 
  • otherwise, we use .catch() to handle anything passed into reject() (‘falsy answer!’).

Promises are fundamental to modern JavaScript asynchronous programming but they can take time to get comfortable with so don’t stress if this feels a bit confusing. With some practice, you’ll be using Promises in no time, we promise! Check out this article for a deeper dive on JavaScript Promises.

Default parameters in ES6

Default parameters are a small, yet incredibly useful change in ES6 that simplify your code and help with error prevention. Instead of having to write extra code to set default values when an argument is missing, like this:



ES6 lets us define default values directly in the function declaration! Like this:

In the example above, the default value of ‘Friend’ is used when the function is called with a missing argument. When called again with ‘Sam’ passed in, the default of ‘Friend’ is overwritten by ‘Sam’. 


Default parameters even allow us to use expressions, and reference other parameters, like this:

In this example:

  • ‘width’ defaults to 60 inches (standard queen size).
  • The length parameter is calculated using an expression that references the width parameter multiplied by 1.33 
  • If there are no arguments provided, the default parameters would evaluate to 60 and 80 so our function would return 4797 square inches. 
  • On line 11, we pass in 76 (standard king width). The default width is overwritten by 76, so our length parameter evaluates to an argument of 101.08, and our function returns 8092.8 square inches.

    That might be a silly example (Sam may or may not have been shopping for a mattress this week), but it perfectly demonstrates the flexibility and simplification that default parameters provide!

The for… of loop in ES6 JavaScript

By now, you’ve probably used a general for loop to iterate over a string or array and access each character or element by its index. A for…of loop iterates over a collection of data and directly accesses its values instead of its keys or indices. Below, a for…of loop is used to access the values of strings and arrays.

Below, see how a for…in loop is used to iterate over an array and access each index (instead of the value of each index).

Note: It is advised to use for…in loops for objects but not for arrays. This is because a for…in loop will iterate through custom properties on an array.

To avoid side effects like the unexpected behavior seen in the example above, only use for…in loops for objects.

ES6 Set objects

Set Objects are data structures released by ES6 that are very similar to arrays. Much like arrays, Set Objects :

  • Use bracket notation. 
  • Can be expanded using the spread operator.
  • Can be iterated through.
  • Support destructuring.
  • Contain iterator methods (though many function differently than array iterator methods).
  • Can use the forEach method to iterate through and perform functions on their values.
  • Can use for…of loops to iterate through their values.

However, there are several key differences between Sets and arrays:

  • Unlike arrays, Set Objects do not allow for duplicate values. 
  • Sets do not support index-based searches (ie: example[0]).
  • Sets do not support array methods such as reduce, map, filter, find, and more. However, they can be converted into arrays and then perform these methods. This can be done by simply using the spread operator.

Below, we see an example of a new Set object being initialized with the label setEx. Notice how only one string of ‘hi’ is found in setEx and, on line 12, we can convert it into an array and assign it to the variable setAsArray.



Another key advantage of using Sets over Arrays is that Sets are more efficient when it comes to looking up their values. Lookup operations in Sets are much faster (usually O(1) - constant). Below, invoking setEx.has() is faster than invoking arrayEx.includes() because the Array.includes() must iterate through the entire array whereas Set.has() does not.

Be sure to check out other useful Set methods like delete(), clear(), size(), forEach(), values(), and more and you’ll quickly find how powerful these data structures can be!

ES6 Map objects

Map objects are another useful data structure in ES6 JavaScript that share some similarities with regular objects, but also offer some improvements in functionality for dealing with key-value pairs. Much like objects, Maps:

  • Use key-value pairs for data storage.
  • Provide the ability to add and update keys and values at runtime.
  • Contain iterator methods like .keys(), .values(), and .entries(), which are similar to Object.keys(obj), Object.entries(obj), etc., but they don’t return arrays and work a bit differently under the hood. 
  • Allow values to be accessed by keys.
  • Allow for the deletion of keys with their corresponding values.

The main differences and improvements that Maps bring to the table, include:

  • Non-string keys: Where normal objects always convert keys into strings, Maps allow for any data type including numbers, strings, objects, and even functions to be used as keys.
  • Insertion order maintenance: Where normal objects will not always guarantee the insertion order of entries to be maintained for iteration, Map objects can always be iterated over in the exact order that the entries were inserted. 
  • Built-in Methods: Normal objects require manual syntax with assignment operators and utility functions to add, update, search, and delete; while Maps have methods like .set(), .get(), .has(), and .delete(), which use a hash table under the hood for constant O(1) lookup time. 

The example below:

  • Creates an object using object literal syntax.
  • Manually adds entries to the object on lines 4 and 5.
  • Accesses elements of the object using dot notation on line 9.
  • Modifies the object's properties using the assignment operator on line 11.

This example would work well for basic cases, but here’s an example of how Map objects open up the realm of possibility:

The example above details the following:

Creating a new Map object:

  •  On line 3, we create a new instance of Map by calling its global constructor, and assign it the label map. 

Adding entries to the Map object:

  • On lines 6-8, the built-in .set() method adds key value pairs to our new Map instance. Notice how each of the keys we’re adding as the first argument to .set() are different data types. 
  • On lines 11-12 we use the .get() method to access our values directly if we know that a key exists within the Map object.

Checking for Keys: 

  • If we are unsure that a key exists within a Map object, we check for it by using the .has() method, as seen on line 15. 

Deleting Entries:

  • On line 18, we use Map’s built-in .delete() method passing in a key to remove a key-value from our Map object. 

Maps are a major efficiency upgrade in working with key-value pairs. Be sure to check out other useful Map methods, like .clear(), .size(), and .forEach().

 

ES6 JavaScript’s global methods

ECMAScript 6 introduced many useful global methods- methods (functions) that can be used anywhere in your code without calling a new instance of an object or class. Some of the most popular global methods in ES6 JavaScript include:

Array.from() - converts data types with length properties into true arrays. You may pass a second argument to it, which can be a map function to transform elements as you convert them into an array.

Array.find() - finds the first instance of an element in an array that satisfies a provided function.

Array.findIndex() - very similar to Array.find() but returns the index of the first element in an array that satisfies a provided function.

String.includes() - returns whether or not a string includes a specified substring.

String.startsWith() - checks if a string starts with the specified substring.

String.endsWith() - checks whether or not a string ends with a specified substring.

Other popular ES6 global methods include:

Number.isNaN() - determines if the passed in value is not a number.

Number.isInteger() - determines whether or not the passed in value is an integer.

Math.sign() - returns whether the passed in number is positive, negative or null.

Math.trunc() - returns a number with any fractional digits removed (ie: 4.25 => 4)

The Global methods in ES6 JavaScript are incredibly useful as they provide an optimized and easily readable standard for executing common tasks in your code. Whether you’re solving algorithms or developing a program, you’ll find yourself using these pre-built functions quite a bit!

ES6 Javascript modules

A wonderfully flexible feature in ES6 Javascript was the introduction of modules. In essence, a module is a Javascript (.js) file that uses the import and export keywords to define dependencies and make variables, objects, and functionality available to other files. This feature allows you to modularize your code by splitting it into reusable pieces.

 

To export items from a module, you can either use a default export, or named exports. The default export is used to make a single primary value available, often when a module has one specific purpose. The keywords export default are used, followed by the value you want to export. For example:

When using named exports, you can make multiple items available to other modules by using the export keyword before the declaration of your function, variable, or object, like this:

To use your exports in another module, you need to bring them into the file with an import declaration. To bring in named exports, use the import keyword, list the names in curly braces, add from, and specify the module’s path in quotes. Importing a default export is similar but doesn’t require curly braces, and you can rename it as long as the origin path is correct. For example:

You can even mix and match imports, combining named exports and the default export from the same module. If you have a group of related functions or items, you can bundle everything into an object to use as the default export, making it all available with a single import

Modules are like the Tupperware of Javascript –a convenient, reusable way to store, organize, and share your code within the codebase. Bonus: no spaghetti stains.

Summary

ES6 JavaScript introduced some of the most groundbreaking features that engineers use in their code every day. From the introduction of the const and let keywords, to spread and rest syntax to ES6 modules, and everything in between, this powerful update has revolutionized the way in which engineers write clean, efficient, and maintainable code. We encourage you to try them out for yourself and check out the Codesmith blog for articles that dive deeper into these ES6 features.

FAQ’s

Is ES6 outdated?

Not at all. By introducing the features mentioned in this article, ES6 has laid the foundation for massive improvements in JavaScript making it more concise and readable. Because of this, ECMAScript has continued to evolve, helping JavaScript maintain its status as the most relevant and popular scripting language today. 

If you’re new to software development, you can take comfort in knowing that you’re starting in the right place with JavaScript as it is relatively straight forward (though some of these concepts may feel a bit confusing at the moment) and it is one of the only languages that can be used for front and back end development. JavaScript also has a massive community of support meaning that you’ll have countless resources to help you learn and grow throughout your journey. Happy scripting!

ES7 JavaScript

When was ES7 released?

ECMAScript7 was released in June of 2016 and is considered to be a relatively incremental update compared to ES6 as it added only a few major features to the language. Although it is not considered to be a major improvement from previous versions, ECMAScript 7 is still regarded as one of many important steps toward JavaScript as we know it today.

What is the difference between ES6 and ES7?

EMCAScript7 offered fewer new features than its predecessor but was still a step toward making JavaScript more functional and readable. The two most notable updates were:

  • The Array.includes method which functions almost exactly the same as the String.includes method released in ES6. A value can be passed in and the method will return a boolean indicating whether or not that value exists within the current array.

  • The Exponential Operator (**) can be used to multiply a number to a desired power.

(ie: console.log(2 ** 3) // logs 8)

Find out how we cover AI/ML in our updated curriculum
Get your Syllabus
Special blog guest offer!

Explore CS Prep further in our beginner-friendly program.

Get 50% Off CS Prep
Learning code on your own?

Get more free resources and access to coding events every 2 weeks.

Thank you for your interest in the syllabus.

We'll be in touch soon with more information!

In the meantime, if you have any questions, don’t hesitate to reach out to our team.

Oops! Something went wrong while submitting the form.
Want to learn more about advancing your career in tech?

Connect with one of our graduates/recruiters.

Schedule a Call

Our graduates/recruiters work at:

ABOUT THE AUTHOR

Sam and Alex are writing partners, content creators, proud Codesmith alumni, and best friends. Former professional dancers/actors, the two transitioned into software development from musical theatre to bring their creative energy and passion for storytelling into the world of tech — specifically in developer relations. Their mission is simple: make software development fun, accessible, and community-driven. Through comedy, empathy, and a deep understanding of what makes content resonate, Alex and Sam strive to build bridges between developers and the tools they love. Sam, a Lead Engineering Fellow at Codesmith, is known among students and colleagues for his empathetic teaching style, strong sense of responsibility toward his students, and infectious sense of humor. Alex, a Technical Content Creator at Codesmith, blends technical knowledge with storytelling and humor to turn complex topics into engaging, easy-to-understand content.

Related Articles

‍How To Structure Your Prompts to Get Better LLM Responses: Understanding the Anatomies of LLM Prompts 

AI/ML
Tutorial
by
Diana Cheung
Mar 20, 2024
|
17 mins

Diagramming System Design: Amazon S3 Storage System

Tutorial
by
Iván Ovejero
Oct 20, 2023
|
15 mins

ECMAScript 6 Tutorial 2025

Tutorial
JavaScript
by
Sam LaRiviere and Alex Stewart
Mar 31, 2025
|
15 mins

Start your journey to a mid- to-senior level coding career.

Thank you for your interest in the syllabus.

We'll be in touch soon with more information!

In the meantime, if you have any questions, don’t hesitate to reach out to our team.

Oops! Something went wrong while submitting the form.
Want to learn more about advancing your career in tech?

Connect with one of our graduates/recruiters to learn about their journeys.

Schedule a Call

Our graduates/recruiters work at: