The with statement extends the scope chain for a statement.
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:
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!
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:
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:
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:
// 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!
const author = authors[0].ref.data[0].data;
console.log(author.name, author.lastName, author.age);
Or:
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.
One property I didn't mention is Symbol.unscopables
, which specify any properties that should not be transported to the highest scope.
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 ✌
Top comments (10)
This seems like a very nice feature in some places. I don't think the arguments (actually, it's just a single argument) against it are justified.
You could have any of these problems in a for loop for example. Does that mean we should abandon the for loop because it COULD lead to readability issues? No. Good tooling prevents you from this kind of problems.
Kotlin seems to do just fine with the "with" statement.
Thanks for the input! Could you elaborate on how you would get the same problem the with statement introduces in a loop, and what "good tooling" can completely prevent it?
P.S. If I'm reading the docs correctly, Kotlin's
with
has completely different functionality wise than Javascript's.You have the exact same problem here. Yet, no one is stating that you shouldn't use the for loop.
All I'm saying is, not using something just because someone said so is sometimes counter-productive. Frankly, this is the first time I've heard of this feature and I'm a little saddened that it's labeled as discouraged.
When comparing to Koltin, they are very similar. The difference is that in Kotlin, this is going to be only the object passed to the with statement, while in JS, this will (effectively) be set to the enclosing this + the object passed to the with statement.
This is the kind of stuff a linter could easily warn you of.
I agree that the "Which is logged?" question isn't particularly useful to determine whether you should us ethe statement or not, since, when creating multiple scopes, you will always run that risk.
However, the much bigger fish I was talking about is when you accidentally reference something you didn't even know existed in the object you use. This problem is not shared with loops since you explicitly write out all variable names, so I don't think your comparison applies.
And what can a linter do? In the example I showed in the post, the linter wouldn't find anything wrong. The only thing linters do is tell you not to use
with
.I had forgotten “with”, now I need to forget it again 😂
😂😂
The python programming language also have
with
, but yes it is really underated.I have rarely seen the use of
with
in programs.beware, docs say: "Warning:Use of the with statement is not recommended, as it may be the source of confusing bugs and compatibility issues."Err did you read the article? That's pretty much the whole point 😉
Ah 😅 Damn, sorry! Nice article!