New JavaScript features you might have missed
Derick Ify Iloabachie
Posted on February 9, 2023
Introduction
Overview of JavaScript
JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions. While it is most well-known as the scripting language for Web pages, many non-browser environments also use it, such as Node.js, Apache CouchDB and Adobe Acrobat. JavaScript is a prototype-based, multi-paradigm, single-threaded, dynamic language, supporting object-oriented, imperative, and declarative (e.g. functional programming) styles.
Importance of keeping up with new JavaScript features
- Improved efficiency and performance
- Better developer experience
- Improved job prospects
- Improved interoperability with other technologies
This article provides an overview of some new features of JavaScript that you might not be familiar with which can help boost productivity.
Private class features
By default, class
fields are public. However, private
class members can now be created using a hash #
prefix.
Private members were not native to JavaScript before this syntax existed
class ClassWithPrivate {
#privateField;
#privateFieldWithInitializer = 'cat';
#privateMethod() {
// - -
}
static #privateStaticField;
static #privateStaticFieldWithInitializer = 42;
static #privateStaticMethod() {
// - -
}
}
Some things worth noting are :
- All private identifiers within a class must be unique
- The private identifier cannot be
#constructor
Static initialization blocks
Static initialization blocks are a special feature of a class that enable more flexible initialization of static properties than can be achieved using per-field initialization.
Static blocks allow statements to be evaluated during initialization, which allows initialization that (for example) include try...catch or set multiple fields from a single value.
class ClassWithStaticInitializationBlock {
static staticProperty1 = 'Property 1';
static staticProperty2;
static {
this.staticProperty2 = 'Property 2';
}
}
console.log(ClassWithStaticInitializationBlock.staticProperty1);
// Expected output: "Property 1"
console.log(ClassWithStaticInitializationBlock.staticProperty2);
// Expected output: "Property 2"
Some things worth noting are :
- A class can have any number of
static {}
initialization blocks in its class body -
var
declarations in the block are not hoisted. Variables declared inside the static block is local to the block.
var y = "Outer y";
class A {
static field = "Inner y";
static {
var y = this.field;
}
}
// var defined in static block is not hoisted
console.log(y); // 'Outer y'
Top level await
You can use the await
keyword on its own (outside of an async function) at the top level of a module. This means that modules with child modules that use await
will wait for the child modules to execute before they themselves run, all while not blocking other child modules from loading.
function setTimeoutAsync(timeout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, timeout);
});
}
// Waits for timeout - no error thrown
await setTimeoutAsync(5000);
// fetch request
const colors = fetch("../data/colors.json").then((response) => response.json());
export default await colors;
Error cause
The cause data property of an Error
instance indicates the specific original cause of the error.
It is useful in catching and re-throwing an error with a more-specific or useful error message in order to still have access to the original error.
try {
connectToDatabase();
} catch (err) {
throw new Error('Connecting to database failed.', { cause: err });
}
The value of
cause
can be of any type.
in
operator check for private fields
The in operator returns true
if the specified property is in the specified object or its prototype chain
// old way
class C {
#x;
static isC(obj) {
try {
obj.#x;
return true;
} catch {
return false;
}
}
}
// new way
class C {
#x;
static isC(obj) {
return #x in obj;
}
}
This generally avoids the need for dealing with error handling just to access a private property that may be nonexistent. However, the
in
operator still requires the private property to be declared beforehand in the enclosing class — otherwise, it would throw aSyntaxError
at()
for indexing
The at() method takes an integer value and returns the item at that index, allowing for positive and negative integers. Negative integers count back from the last item in the array or string.
const array1 = [5, 12, 8, 130, 44];
let index = 2;
console.log(`Using an index of ${index} the item returned is ${array1.at(index)}`);
// Expected output: "Using an index of 2 the item returned is 8"
index = -2;
console.log(`Using an index of ${index} item returned is ${array1.at(index)}`);
// Expected output: "Using an index of -2 item returned is 130"
The at()
method is equivalent to the bracket notation when index is non-negative. For example, array[0]
and array.at(0)
both return the first item. However, when counting elements from the end of the array, you cannot use array[-1]
because all values inside the square brackets are treated literally as string properties.
The usual practice is to access length and calculate the index from that — for example, array[array.length - 1]
. The at()
method allows relative indexing, so this can be shortened to array.at(-1)
.
// array with items
const cart = ["apple", "banana", "pear"];
// A function which returns the last item of a given array
function returnLast(arr) {
return arr.at(-1);
}
// Get the last item of array 'cart'
const item1 = returnLast(cart);
console.log(item1); // 'pear'
// Add an item to our 'cart' array
cart.push("orange");
const item2 = returnLast(cart);
console.log(item2); // 'orange'
Bonus
Logical AND (&&)
Read on and you might just be impressed with what you would learn about logical operators return values.
The logical AND (&&) (logical conjunction) operator for a set of boolean operands will be true
if and only if all the operands are true
. Otherwise it will be false
.
More generally, the operator returns the value of the first falsy
operand encountered when evaluating from left to right, or the value of the last operand if they are all truthy
.
a1 = true && true; // t && t returns true
a2 = true && false; // t && f returns false
a3 = false && true; // f && t returns false
a4 = false && 3 === 4; // f && f returns false
a5 = "Cat" && "Dog"; // t && t returns "Dog"
a6 = false && "Cat"; // f && t returns false
a7 = "Cat" && false; // t && f returns false
a8 = "" && false; // f && f returns ""
a9 = false && ""; // f && f returns false
Logical OR (||)
The logical OR (||) (logical disjunction) operator for a set of operands is true if and only if one or more of its operands is true. It is typically used with boolean (logical) values. When it is, it returns a Boolean value. However, the || operator actually returns the value of one of the specified operands, so if this operator is used with non-Boolean values, it will return a non-Boolean value.
true || true; // t || t returns true
false || true; // f || t returns true
true || false; // t || f returns true
false || 3 === 4; // f || f returns false
"Cat" || "Dog"; // t || t returns "Cat"
false || "Cat"; // f || t returns "Cat"
"Cat" || false; // t || f returns "Cat"
"" || false; // f || f returns false
false || ""; // f || f returns ""
false || varObject; // f || object returns varObject
Note: If this operator is used to provide a default value to some variable, be aware that any
falsy
value will not be used.
Browser compatibility issues
When using new JavaScript features, it's important to consider browser compatibility because not all browsers may support the latest features.
Here are some things to keep in mind:
- Browser compatibility matrix: You can check the browser compatibility matrix for the latest JavaScript features on websites like caniuse.com or the MDN web docs
- Polyfills and transpilation: If a new JavaScript feature is not supported by a specific browser, you can use a polyfill or transpile your code to a version of JavaScript that is compatible with the target browser.
In summary, when using new JavaScript features, it is important to consider browser compatibility and take the necessary steps to ensure your code works in a variety of browsers. This may involve using polyfills, transpilation, feature detection, or a combination of these approaches.
Conclusion
This article has served as an overview of new features added to the JavaScript programming language. You learned about some new features and the benefits of staying up to date with JavaScript which you can leverage when working on a new (or existing project).
In my opinion, keeping up with cutting-edge technologies can dramatically improve productivity.
Posted on February 9, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.