5 Exciting New JavaScript Features in 2024

Share this article

Exciting New JavaScript Features in 2024

In this article, we’ll explore some of the most exciting and hotly anticipated JavaScript features that are expected to land in 2024.

The following proposals stand a good chance of making it into this year’s version of ECMAScript:

Table of Contents

ECMAScript Updates

A new version of JS always causes a stir. Since the ES6 update there has been a new version every year, and we’re expecting this year’s (ES2024) to land around June.

ES6 was a massive release that came six years after its predecessor, ES5. Browser vendors and JavaScript developers were overwhelmed with the sheer number of new features to adopt and learn. Since then, to prevent such a big drop of new features happening at once, there’s been a yearly release cycle.

This yearly release cycle involves proposing any new features, which are then discussed, evaluated, then voted on by a committee before they’re added to the language. This process also allows browsers to try to implement the proposals before they’re officially added to the language, which may help iron out any implementation problems.

As mentioned, new features for JavaScript (or ECMAScript) are decided by Technical Committee 39 (TC39). TC39 is made up of representatives from all the major browser vendors as well as JavaScript experts. They meet regularly to discuss new features for the language and how they can be implemented. The new features are put forward as proposals (made by anyone) and the committee members then vote on whether each proposal can move forward to the next stage. There are 4 Stages for each proposal; once a proposal reaches Stage 4, it’s expected to be included in the next version of ES.

An important part of the ES specification is that it has to be backwards compatible. This means that any new features can’t break the Internet by changing how previous versions of ES worked. So they can’t change how existing methods work, they can only add new methods, as any website running with a potentially pre-existent method would be at risk of breaking.

The full list of all the current proposals can be seen here.

Temporal

In the State of JS 2022 survey, the third most common answer to “What do you feel is currently missing from JavaScript?” was Better Date Management.

This has led to the Temporal proposal, which offers a standard global object to replace the Date object and fixes a number of the issues that have caused developers much pain when working with dates in JavaScript over the years.

Working with dates in JavaScript is almost always a dreaded task; having to deal with small but infuriating inconsistencies, such as the craziness of months being zero-indexed but days of the month starting at 1.

The difficulty of dates has resulted in popular libraries such as Moment, Day.JS and date-fns popping up to try to fix the issues. However, the Temporal API aims to fix all the problems natively.

Temporal will support multiple time-zones and non-Gregorian calendars out of the box, and will provide a simple-to-use API that will make it much easier to parse dates from strings. Furthermore, all Temporal objects will be immutable, which will help avoid any accidental date change bugs.

Let’s look at some examples of the most useful methods offered by the Temporal API.

Temporal.Now.Instant()

Temporal.Now.Instant() will return a DateTime object to the nearest nanosecond. You can specify particular dates using the from method like so:

const olympics = Temporal.Instant.from('2024-07-26T20:24:00+01:00');

This will create a DateTime object that represents the start of the Paris Olympics later this year at 20:24 on the 26th July 2024 (UTC).

PlainDate()

This allows you to create just a date, with no time:

new Temporal.PlainDate(2024, 7, 26);

Temporal.PlainDate.from('2024-07-26');

// both return a PlainDate object that represents 26th July 2024

PlainTime()

As a complement to PlainDate(), we can use this to create just a time with no date, using .PlainTime():

new Temporal.PlainTime(20, 24, 0);

Temporal.PlainTime.from('20:24:00');

// both return a PlainTime object of 20:24

PlainMonthDay()

PlainMonthDay() is similar to PlainDate, but it only returns the month and day with no year information (useful for dates that recur on the same day every year, such as Christmas Day and Valentine’s Day):

const valentinesDay = Temporal.PlainMonthDay.from({ month: 2, day: 14 });

PlainYearMonth()

Similarly, there’s also PlainYearMonth that will return just the year and month (useful for representing a whole month of a year):

const march = Temporal.PlainYearMonth.from({ month: 3, year: 2024 });

Calculations

There are a number of calculations that can be done with Temporal objects. You can add and subtract various units of time to a date object:

const today = Temporal.Now.plainDateISO();

const lastWeek = today.subtract({ days: 7});

const nextWeek = today.add({ days: 7 });

The until and since methods let you find out how much time until a certain date or since the date occurred. For example, the following code will tell you how many days it is until the Paris Olympics:

olympics.until().days

valentinesDay.since().hours

These methods return a Temporal.Duration object that can be used to measure an amount of time that has numerous different units and rounding options.

Extras

You can extract the year, month and day from a Date object and the hours, minutes, seconds, milliseconds, microseconds and nanoseconds form a Time object (microseconds and nanoseconds are not available in the current DateTime object). For example:

olympics.hour;
<< 20

There are also other properties such as dayOfWeek (returns 1 for Monday and 7 for Sunday), daysInMonth (returns 28,29,30 or 31 depending on the month) and daysinYear (returns 365 or 366 depending on a leap year).

Temporal date objects will also have a compare method that can be used to order dates using various sorting algorithms.

Temporal is currently a Stage 3 proposal that’s in the process of being implemented by browser vendors, so it seems as if its time has come (pun intended). You can see the full documentation here. There’s also a useful cookbook of use cases here. When paired with the Intl.DateTimeFormat API you’ll be able to do some very nifty date manipulation.

Pipe Operator

In the State of JS 2022 survey, the sixth top answer to “What do you feel is currently missing from JavaScript?” was a Pipe Operator.

You can see the Pipe Operator proposal here.

A pipe operator is a standard feature in functional languages that allows you to “pipe” a value from one function to another, with the output of the previous function being used as the input to the next (in a similar way that the Fetch API passes any data it returns from one promise to the next).

For example, say we wanted to consecutively apply three functions to a string:

  1. Concatenate the string “Listen up!” to the beginning of the original string.
  2. Concatenate three exclamation marks onto the end of the string.
  3. Make all the text upper case.

These three functions could be written as follows:

const exclaim = string => string + "!!!"
const listen = string => "Listen up! " + string
const uppercase = string => string.toUpperCase()

These three functions could be applied by nesting them all together as follows:

const text = "Hello World"

uppercase(exclaim(listen(text)))
<< "LISTEN UP! HELLO WORLD!!!"

But deeply nesting multiple function calls like this can get messy very quickly, especially since the value (text) being passed as an argument ends up deeply embedded inside the expression, making it difficult to identify.

The other problem with function nesting is that the order the functions are applied in is back to front, in that the inner-most functions are applied first. So in this case, listen gets applied to the original value of text, followed by exclaim, then the outer-most function, uppercase, will be applied last of all. Particularly for large and complex functions, this becomes hard and unintuitive to follow.

An alternative is to use function chaining like this:

const text = "Hello World"

text.listen().exclaim().uppercase()

This solves a lot of problems from nested functions. The argument being passed is at the beginning, and each function appears in the order it’s applied in, so listen() is applied first, then exclaim() then uppercase().

Unfortunately, this example won’t work, because the listen, exclaim and uppercase functions aren’t methods of the String class. They could be added by monkey patching the String class, but this is generally frowned on as a technique.

This means that, although chaining looks a lot better than function nesting, it can only really be used with built-in functions (as is frequently done with Array methods).

Piping combines the ease of use of chaining but with the ability to use it with any functions. Under the current proposal, the example above would be written like so:

 text |> listen(%) |> exclaim(%) |> uppercase(%)

The % token is a placeholder used to represent the value of the output of the previous function, although it’s highly likely that the % character will be replaced by some other character in the official release. This allows for functions that accept more than one argument to be used along the pipeline.

Piping combines the ease of chaining but can be used with any custom functions that you’ve written. The only condition is that you need to ensure that the output type of one function matches the input type of the next function in the chain.

Piping works best with curried functions that only accept a single argument that’s piped from the return value of any previous function. It makes functional programming much easier, as small, building-block functions can be chained together to make more complex composite functions. It also makes partial application easier to implement.

Despite its popularity, the pipe operator has struggled to move forward beyond Stage 2 of the process. This is due to disagreements over how the notation should be expressed and concerns over memory performance and how it might work with await. It seems that the committee is slowly reaching some sort of agreement, though, so hopefully the pipe operator might move quickly through the stages and make an appearance this year.

Thankfully, the pipeline operator has been implemented in Babel from version 7.15.

Personally, we would love the pipe operator to be implemented and rolled out this year, as it would really help improve the credentials of JavaScript as a serious functional programming language.

Records and Tuples

The Record and Tuple proposal aims to bring immutable data structures to JavaScript.

Tuples are similar to arrays — an ordered list of values — but they’re deeply immutable. This means that every value in a tuple must either be a primitive value or another record or tuple (not arrays or objects, because they are mutable in JavaScript).

A tuple is created in a similar way to an array literal, but with a leading hash symbol (#) at the front:

const heroes = #["Batman", "Superman", "Wonder Woman"]

Once this has been created, no other values can be added and no values can be removed. The values cannot be changed either.

Records are similar to objects — a collection of key-value pairs — but they’re also deeply immutable. They’re created in a similar way to an object — but in the same way as tuples, they start with a leading hash:

const traitors = #{
  diane: false,
  paul: true,
  zac: false,
  harry: true
}

Records will still use the dot notation to access properties and methods:

traitors.paul
<< true

And the square bracket notation that arrays use can also be used for tuples:

heroes[1]
<< "Superman"

But since they’re immutable, you can’t update any of the properties:

traitors.paul = false
<< Error

heroes[1] = "Supergirl"
<< Error

The immutability of tuples and records means that you’ll be able to compare them easily using the === operator:

heroes === #["Batman", "Superman", "Wonder Woman"];
<< true

One thing to note is that the order of properties doesn’t matter when considering the equality of records:

traitors === #{
  ross: false,
  zac: false,
  paul: true,
  harry: true
};
// still true, even though the order of people has changed
<< true

The order does matter for tuples, though, as they’re an ordered list of data:

heroes === #["Wonder Woman", "Batman", "Superman"];
<< false

This page has a handy tutorial with a live playground so you can get used to how records and tuples will work.

RegExp /v flag

Regular expressions have been incorporated in JavaScript since version 3, and there have been numerous improvements since then (such as Unicode support using the u flag in ES2015). The v flag proposal aims to do everything the u flag does, but it adds some extra benefits that we’ll look at in the examples below.

Simply, implementing the v flag involves adding a /v to the end of your regular expression.

For example, the following code can be used to test if a character is an emoji:

const isEmoji = /^\p{RGI_Emoji}$/v;
isEmoji.test("💚");
<< true

isEmoji.test("🐨");
<< true

This uses the RGI_Emoji pattern to identify emojis.

The v flag also allows you to use set notation in your regular expressions. For example, you can subtract one pattern from another using the -- operator. The following code can be used to remove any love hearts from the set of emojis:

const isNotHeartEmoji = /^[\p{RGI_Emoji_Tag_Sequence}--\q{💜💚♥️💙🖤💛🧡🤍🤎}]$/v;

isNotHeartEmoji.test("💚");
<< false

isNotHeartEmoji.test("🐨");
<< true

You can find the intersection of two patterns using &&. For example, the following code will find the intersection of Greek symbols and letters:

const GreekLetters = /[\p{Script_Extensions=Greek}&&\p{Letter}]/v;

GreekLetters.test('π');
<< true

GreekLetters.test('𐆊');
<< false

The v flag also irons out some issues that the u flag had with case insensitivity as well, making it a much better option to use in almost all cases.

The v flag for regular expressions reached Stage 4 during 2023 and has been implemented in all major browsers, so it’s fully expected to be part of the ES2024 specification.

Decorators

The Decorator proposal aims to use decorators to extend JavaScript classes natively.

Decorators are already common in many object-oriented languages such as Python and have already been included in TypeScript. They are a standard metaprogramming abstraction that allows you to add extra functionality to a function or class without changing its structure. For example, you might want to add some extra validation to a method, and you could do this by creating a validation decorator that checks the data entered into a form.

Whilst JavaScript lets you use functions to implement this design pattern, most object-oriented programmers would prefer a simpler and native way of achieving this, simply to make life much easier.

The proposal adds some syntactic sugar to allow you to easily implement a decorator inside a class without having to think about binding this to the class. It provides a much cleaner way of extending class elements, such as class fields, class methods, or class accessors, and it can even be applied to the whole class.

Decorators are identified with a prefix of the @ symbol and are always placed immediately before the code they’re “decorating”.

For example, a class decorator will come immediately before the class definition. In the example below, the validation decorator is applied to the whole of the FormComponent class:

@validation
class FormComponent {
  // code here
}

// The decorator function also needs defining
function validation(target) {
  // validation code here
}

A class method decorator comes immediately before the method it decorates. In the example below, the validation decorator is applied to the submit method:

class FormComponent {
  // class code here

  @validation
  submit(data) {
    // method code here
  }
}

// The decorator function also needs defining
function validation(target) {
  // validation code here
}

Decorator function definitions accept two parameters: a value and context. The value argument refers to the value being decorated (for example a class method) and the context contains metadata about the value, such as if it’s a function or not, its name, and if it’s static or private. You can also add an initializer function to the context that will be run when a class is instantiated.

The Decorator proposal is currently in Stage 3 and has been implemented in Babel, so you can already try it out.

Conclusion

So what do you think? What would you like to see added to the spec this year? All these features will make great additions to JavaScript, so fingers crossed they’ll make it in this year!

Olivia GibsonOlivia Gibson
View Author

Olivia Gibson is a student of Maths and Computer Science. Over the past year, she has immersed herself in the world of web development and loves coding in JavaScript and Python. Some of her highlights include, Numble and German Flashcards.

Darren JonesDarren Jones
View Author

Darren loves building web apps and coding in JavaScript, Haskell and Ruby. He is the author of Learn to Code using JavaScript, JavaScript: Novice to Ninja and Jump Start Sinatra.He is also the creator of Nanny State, a tiny alternative to React. He can be found on Twitter @daz4126.

ECMAScript
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week