New JavaScript features you might have missed
Tags: javascript, programming, node
Published: 2023-02-09 13:18:10

## 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 a `SyntaxError`


#### `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](https://caniuse.com) or the [MDN web docs](https://developer.mozilla.org)
- 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.