The forgotten `with` statement — and why you shouldn't use it
Tags: javascript, programming, productivity, codequality
Published: 2021-10-12 22:52:00

> The **with statement** extends the scope chain for a statement.

\- [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with)

The `with` statement was originally implemented to reduce verbosity and when dealing with long object references. If the object is particularly expensive performance-wise, it will also save the compiler from having to parse it multiple times.

You could do this:
```js
const name = authors[0].ref.data[0].data.name;
const lastName = authors[0].ref.data[0].data.lastName;
const age = authors[0].ref.data[0].data.age;

console.log(name, lastName, age);
```
Or, you could just do this!
```js
with (authors[0].ref.data[0].data) {
  // every property of the given object
  // is transported to the top of the scope
  // and can be referenced directly
  console.log(name, lastName, age);
}
```

Here's a more practical example that you might find a use for in everyday work:

```js
with(document) {
  with(documentElement) {
    insertBefore(
      createElement("script"),
      firstChild
    )
    .text = "alert(1)"
  }
}
```

I personally only found out about this keyword a week a go; probably because it has been excluded from almost *all modern code*. In ES5, it's even **forbidden** in strict mode 🤯

## Why?

Primarily, it's confusing and can easily lead to bugs. Consider the code below:

```js
const foo = 'bar';
with ({ foo: 'baz' }) {
  console.log(foo);
}
```
Do you think `'bar'` will be logged, or do you think `'baz'` will be logged? As you can see, this can lead to readability issues. Consider another segment of code below:

```js
// whether we should show the answer of a riddle
const hidden = true;

with (document) {
  const riddleDisplay = getElementById('riddle');
  const [question, answer] = riddleDisplay.children;

  if (hidden) {
    answer.style.display = 'none';
  }

  // ...
}
```

Pretty easy to understand, right? Nope! Actually, unbeknownst to both the reader and the writer, `hidden` would be referencing *`document.hidden`*.

Maybe you're paying close attention, are well versed in the `Document` object, and knew this would happen. But what if you *didn't* know every property of an object? Maybe it's from an external library, or you missed as a property was assigned to it higher in the file.

> If you can't read a program and be confident that you know what it is going to do, you can't have confidence that it is going to work correctly. For this reason, the `with` statement should be avoided...

\- Mr. Crockford

## Substitutions

Instead of using the `with` statement, you could assign the reference to a variable, or use array destructuring!

```js
const author = authors[0].ref.data[0].data;
console.log(author.name, author.lastName, author.age);
```

Or:

```js
const { name, lastName, age } = authors[0].ref.data[0].data;
console.log(name, lastName, age);
```

Simple, readable, and without any uncertain implications.

## Fun Fact

In my previous blog post, I talked about static `Symbol` properties and how they can be combined with classes.

{% post lioness100/javascript-symbols-classes-2b08 %}

One property I *didn't* mention is `Symbol.unscopables`, which specify any properties that should *not* be transported to the highest scope.

```js
const foo = {
  bar: 'baz',
};

object[Symbol.unscopables] = {
  bar: true,
};

with (foo) {
  console.log(bar); // Error - bar is not defined
}
```

---

I hope you learned a bit about the `with` statement! If you have any questions, corrections, or addons, I would love to hear them. Peace ✌