Codementor Events

Complete Guide of New JavaScript Features from ECMAScript 2023

Published Sep 30, 2023Last updated Oct 02, 2023

Complete Guide of New JavaScript Features from ECMAScript 2023

Photo by Joan Gamell on Unsplash

What is ECMAScript and how does it relate to JavaScript?

ECMAScript is a scripting language specification that outlines the features and syntax they must adhere to during implementation. In other words, ECMAScript provides a set of rules, syntax, and features that a scripting language must adhere to in order to be considered compliant with the ECMAScript standard.

JavaScript is one of those languages that adheres to this standard. JavaScript engines interpret and run JavaScript code according to the ECMAScript specification. V8 is a Javascript engine for Google Chrome and Node.js, while SpiderMonkey is the engine for Firefox. Some JavaScript engines have unique implementations, but their primary goal is to adhere to the ECMAScript standard as best as possible for consistent behavior across platforms and browsers.

ECMAScript 2023 (aka ES2023 or ES14) was released in June of 2023. Chrome has supported these new features since version 110, but be sure to check that these features are available in other runtime environments before using them in your project. Click on the header for each feature in this blog to be directed to the MDN documentation to check browser support and more in depth information. Most AI coding tools, like chatGPT-3.5, are not yet aware of these new features since they came out after their data models have been trained.

Ecma International posted the following statement on their site regarding this release:

ECMAScript 2023, the 14th edition, introduced the toSorted, toReversed, with, findLast, and findLastIndex methods on Array.prototype and TypedArray.prototype, as well as the toSpliced method on Array.prototype; added support for #! comments at the beginning of files to better facilitate executable ECMAScript files; and allowed the use of most Symbols as keys in weak collections.


Immutable Array Methods

With the ES14 version, Array.prototype added 4 new methods that change by copy: toReversed, toSorted, toSpliced, and with. These are immutable array methods, which make a copy of the array with the applied modifications without affecting the original array that they were called on.

Any array method that creates a copy, always does so shallowly. If there is an object in the array, the object reference is copied into the new array. In this case, both the original and new array refer to the same object. So when the object changes, all properties referring to the object reflect the change. Keep this in mind when using these methods if you have nested data in your array. Primitive types such as strings, numbers and booleans, are copied by value into the new array, so there is no reference to the original array.

Immutable array methods are very useful when working with state and props in JavaScript libraries like React and Redux. React relies on immutable props and not changing a state value directly in order to determine when to re-render components and with what values. Mutating state directly can lead to unnecessary re-renders, as well as make it difficult to debug and test.

toReversed

// Syntax  
toReversed()

The toReversed() Array method is the copying counterpart of reverse(). It returns a new array with the elements in reversed order and does not take in any parameters.

const originalArray = [1, 2, 3, 4, 5];  
  
// toReversed  
const newArray = originalArray.toReversed();  
  
console.log(originalArray); // Output: [1, 2, 3, 4, 5] the array is unmodified  
console.log(newArray); // Output:[5, 4, 3, 2, 1]  
  
// old ways to create a reversed array without modification  
  
// slice and reverse  
const copy = originalArray.slice();  // make shallow copy  
const newArray2 = copy.reverse();  // Reverse the copy  
  
console.log(originalArray); // Output: [1, 2, 3, 4, 5]  
console.log(newArray2); // Output: [5, 4, 3, 2, 1]  
  
// spread  
const copy2 = [...originalArray]; // copy values of original array into new array  
const newArray3 = copy2.reverse();  
console.log(originalArray); // Output: [1, 2, 3, 4, 5]  
console.log(newArray3); // Output: [5, 4, 3, 2, 1]  

toSorted

// Syntax  
toSorted()  
toSorted(compareFn)

toSorted has the same signature as sort, but it creates a new sorted array instead of sorting the array that it was called on. It returns a new array with the elements sorted in ascending order. The toSorted method takes in one optional parameter, which is a caparison function that defines the sort order. If this parameter is left out, the array elements are converted to strings and sorted according to each character’s Unicode code point value. Therefore, make sure you include a comparison function when sorting numbers. When used on sparse arrays, the toSorted method iterates empty spots as if the value isundefined.

const letters = ["D", "A", "E", "C", "B"]  
const numbers = [4, 2, 5, 1, 3]  
  
// toSorted  
const sortedLetters = letters.toSorted();  
console.log(letters) // Output: ["D", "A", "E", "C", "B"]  
console.log(sortedLetters) // Output: ["A", "B", "C", "D", "E"]  
  
const sortedNumbers = numbers.toSorted((a, b) => a - b)  
console.log(sortedNumbers) // Output: [1, 2, 3, 4, 5]  
  
// common mistake using numbers  
const nums2 = [0, 15, 5, 10, 20]  
const sortedNums2 = nums2.toSorted()  
console.log(sortedNums2) // Output: [0, 10, 15, 20, 5]  

// sort method changes array that it called on  
letters.sort()  
console.log(letters); // Output: ["A", "B", "C", "D", "E"]

toSpliced

// Syntax  
toSpliced(start)  
toSpliced(start, deleteCount)  
toSpliced(start, deleteCount, item1)  
toSpliced(start, deleteCount, item1, item2)  
toSpliced(start, deleteCount, item1, item2, /* …, */ itemN)

toSpliced is the copying version of the splice method. toSpliced removes and/or replaces elements at a given starting index into a new array without modifying the original. The first parameter (start) is a zero based index where changes to the array begins. A negative start parameter counts backwards from the end. Also, if start is greater than the length of the array, no elements will be deleted, but toSpliced will add all of the items after the second argument to the end of the array. deleteCount is the number of elements in the array to remove from start. If deleteCount is not included, or if the value is ≥ the number of elements after start, then all the elements from start to the end of the array will be deleted. If you want to to intentionally do this, you should pass Infinity as deleteCount since undefined gets converted to 0. After the first 2 parameters, you can include items (item1, …, itemN) that will be inserted into the array, beginning from start.

const numbers = [1, 2, 6, 6, 7]  
  
// toSpliced method  
// starting at index 2, delete 1 element, then add the values - 3, 4, and 5  
const result = numbers.toSpliced(2, 1, 3, 4, 5)   
console.log(numbers) // Output: [1, 2, 6, 6, 7]  
console.log(result) // Output: [1, 2, 3, 4, 5, 6, 7]  
  
// delete all values after start and insert values  
const result2 = numbers.toSpliced(2, Infinity, 3, 4, 5)  
console.log(result2) // Output: [1, 2, 3, 4, 5]  
  
// start >= array length, delete is ignored, but items are added  
const result3 = numbers.toSpliced(999, 1, 3, 4, 5)  
console.log(result3) // Output: [1, 2, 6, 6, 7, 3, 4, 5]

with

// Syntax  
arrayInstance.with(index, value)

The with method of Array instances is the copying version of using the bracket notation (array[index] = newValue). The with method updates a single element at a given index and returns a new array. This method takes in an index and the value to be inserted at that index.

const numbers = [1, 2, 9999, 4]  
  
const result = numbers.with(2, 3)  
console.log(numbers) // Output: [1, 2, 9999, 3]  
console.log(result) // Output: [1, 2, 3, 4]  
  
  
// bracket notation modifies the original array  
numbers[2] = 3;  
console.log(numbers) // Output: [1, 2, 3, 4]  
  
// Old ways to create a new array with one value swapped at an index  
// using map  
const originalArray1 = [1, 2, 3, 4, 5];  
const indexToReplace = 2;  
const newValue = 10;  
  
const newArray1 = originalArray1.map((element, index) => {  
  if (index === indexToReplace) {  
    return newValue;  
  }  
  
  return element;  
});  
  
console.log(newArray1); // Output: [1, 2, 10, 4, 5]  
  
// using spread and slice  
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];  
const arr2 = [...arr.slice(0, 1), "hey", ...arr.slice(2)]  
console.log(arr2) // Output: [1, "hey", 3, 4, 5, 6, 7, 8, 9]

Array Last Methods

If you know that a piece of data was towards the end of an array, findIndex and findLastIndex would be more efficient and simple than their counterparts find and findIndex. Before ES14, if you wanted to find the last element or index in an array that satisfies some condition, you would have to either reverse the array first or use a for loop that starts from the end.

findLast

// Syntax  
findLast(callbackFn)  
findLast(callbackFn, thisArg)

The findLast() Array method begins iterating from the end of the array and returns the value of the first element that satisfies the conditions in the callback function. If no elements satisfy the testing function, undefined is returned. Each element of the array is passed into the callback function one by one.

const numsArray = [4, 51, 40, 99, 16]  
  
const lastBigNum = numsArray.findLast((num) => num > 50)  
  
console.log(lastBigNum) // Output: 99

findLastIndex

// Syntax  
findLastIndex(callbackFn)  
findLastIndex(callbackFn, thisArg)

The findLastIndex() Array method iterates over the array in reverse order and returns the index of the first element that satisfies the provided testing callback function. If no elements satisfy the conditions of the function, then -1 is returned. JavaScript returns -1 since an array cannot have a negative index, which lets you know that there were no matching elements in that array.

const numsArray = [4, 51, 40, 99, 16]  
const lastBigNumIndex = numsArray.findLastIndex((num) => num > 50)  
  
console.log(lastBigNumIndex) // Output: 3

All the array methods introduced in ES14 except for toSpliced(), also apply to TypedArray.prototype. In JavaScript, a TypedArray is an array-like object that provides a way to work with binary data in a typed manner. Click here to learn more about TypedArray.


#! Hashbang (aka Shebang) Comments Support

// Syntax  
#! interpreter [optional-arg]

Hashbang comments begins with #! and is only valid at the absolute start of a script or module. No whitespace is permitted before the #!. The standardization of hashbangs in executable scripts now allows JavaScript files to be run directly from the command line. Now, you can run JavaScript code with the command ./fileName.js instead of something like node fileName.js. First, make sure that you have permissions to execute the file by running the command: ls -l filename.js. To add execute permissions, run the command: chmod +x fileName.js.

#!/usr/bin/env node  
  
console.log("Javascript is so cool!")  
// Output: Javascript is so cool!

To run the file above, just run the commend: ./fileName.js
and it will log out: Javascript is so cool!.


Symbols as Keys in Weak Collections

ES14 allows non-registered Symbols to be used as keys in weak collections, such as WeakMap, WeakRef, and WeakSet. Previously, only objects were allowed as keys in these collections. Weak collections are data structures that do not keep a strong reference to its elements. This means that if no other references to a value stored in the weak collection exist, then those values can be garbage collected. Unlike most primitive data types, objects and non-registered symbols can be stored as keys on weak collections because they are garbage-collectable. Weak collections are useful for storing objects that you do not want to keep alive, such as temporary metadata on objects or objects that are only used as a key in a map. A Symbol is a unique and immutable value often used as an object property key, since keys should be unique. Only non-registered Symbols can be used as keys in weak collections though. A registered Symbol is one that was created by using the Symbol.for() method, which adds the symbol to a global registry, therefore making it unable to be garbage collected. More commonly, a Symbol is created like Symbol(“description”), which has a scope limited to the current JavaScript file or module, unless it is exported and imported into another.

const weakMapIds = new WeakMap()  
const key = Symbol("userId")  
  
weakMapIds.set(key, "123abc")  
  
console.log(weakMapIds.get(key))// Output: 123abc

Great job! You made it through! Check out the MDN docs to learn more about each feature by clicking on anything in this article that is underlined. Be sure to also try each of these out for yourself!

Feel free to get in touch with me on LinkedIn! Also, please book a session on codementor.io with me if you would like mentoring or would like me to work on a project with you freelancing. Please like, comment, and follow if you like what you read. Have fun coding!

LinkedIn: linkedin.com/in/rashaun-warner/

Discover and read more posts from Rashaun Warner
get started