JavaScript's Forgotten Keyword (with)
Tags: javascript
Published: 2021-07-29 19:22:27

Being a JavaScript developer can be an exciting job. Almost every day you will find something mysterious and otherworldly. Sometimes that's a magical experience, other times it's downright frightening.

In this article we will take a look at the ["with" keyword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with). It's one of those dark corners of the language that even veteran developers often are not aware of.

## Usage

Let's use the **with** keyword to help us log a message to the console:

```js
with (console) {
  log('I dont need the "console." part anymore!');
}
```

And let's use it to help us join an array into a string:

```js
with (console) {
  with (['a', 'b', 'c']) {
    log(join('')); // writes "abc" to the console.
  }
}
```

Yes my friends, believe it or not, this is JavaScript.

## What "with" Does

Here is the [explanation from MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with):

> JavaScript looks up an unqualified name by searching a scope chain associated with the execution context of the script or function containing that unqualified name. The 'with' statement adds the given object to the head of this scope chain during the evaluation of its statement body. If an unqualified name used in the body matches a property in the scope chain, then the name is bound to the property and the object containing the property. Otherwise a ReferenceError is thrown.

To reword that hopefully a little more simply: when you write an identifier in your code (like `log` or `join` in the above code snippet) there is a chain of objects that JavaScript looks at, and if one of those objects has a property with the same name as the identifier you wrote in your code, JavaScript uses the value of that property.

The `with` keyword lets you inject any arbitrary object into the front of that chain. Here's another example that might make that clearer:

```js
with ({ myProperty: 'Hello world!' }) {
  console.log(myProperty); // Logs "Hello world!"
}
```

## Don't Use It

Awesome, right? Yeah, well, maybe not.

In most cases, the same effect can be achieved just by using temporary variables, which is easier than ever since the advent of [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment).

In addition, [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with) lists a number of pitfalls:

### It's Forbidden in Strict Mode

You can't use `with` in [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with). Considering that [ES modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) and classes are automatically in strict mode, this limitation kills even the possibility of using `with` in many modern use cases.

### Accidental Shadowing

Consider the following code to average two numbers and round the result to an integer:

```js
function getAverage(min, max) {
  with (Math) {
    return round((min + max) / 2);
  }
}

getAverage(1, 5);
```

This returns `NaN`. Why? Because `Math.min()` and `Math.max()` shadow the function's arguments, so we end up calculating the sum of two functions, which of course, is NaN.

Basically if you use `with`, you suddenly have to be more careful about choosing identifiers. You have to go and look at what you are passing into `with` to make sure it does not have properties that might inadvertently shadow something else in a higher scope.

This behavior can also introduce security vulnerabilities. If an attacker is able to add properties to the object you pass to `with`, then they can shadow your identifiers and modify the behavior of your code in unexpected ways.

As one example, passing an object that was parsed from an unvalidated JSON HTTP request body to `with` would be extremely dangerous.

### Performance

By adding something to the scope chain, you slow down just about every line of code, because you are increasing the number of objects that need to be searched to resolve your identifiers to values.

### Ostracism

If you use the `with` keyword, everyone will think you are crazy and avoid you in the lunch room. Or maybe they will just look at you funny.

Either way, using magical language features that no one else knows about will make your code harder to maintain, and in this case won't gain you much.

## Conclusion

The `with` keyword adds some interesting capabilities to the language, but ultimately it comes with too many downsides, and too few advantages, for me to be able to recommend using it.

Of course, don't just take it from me. [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with) seems to hate it with a passion, and it is forbidden in strict mode for a reason.

I have been writing JavaScript for well over five years, and it amazes me that to this day I'm still learning language keywords that are not even remotely new. What else might be lurking out there?

Whence came `with`? Who envisioned it? Why? Did they want something like C++ namespaces? Did a Ouija board tell them to do it?

Whatever the case may be, it looks like the long-forgotten `with` statement will forever be relegated to the dustbin of history.

Though like many dark arts, it is fun to mess with!