JavaScript and the with statement
A controversial feature in the JavaScript language is the with statement. Ill advised by some, tapped of it's magic by others. Is it safe to use or not?
When passing an object to with you temporarily gain shortcut access to its variable scope. It can be useful at times, but it may cause you more trouble than good. Imagine the following scenario:
var el = document.getElementById('el');
el.style.backgroundColor = '#000';
el.style.color = '#fff';
el.style.width = '200px';
el.style.padding = '20px';
The above is perfectly legal code, but lets rewrite it a bit.
with(document.getElementById('el').style) {
backgroundColor = '#000';
color = '#fff';
width = '200px';
padding = '20px';
}
Personally I think it's a pretty clean approach. Although the workings are completely different, it somewhat resembles the way jQuery is used when styling elements:
$("#el").css({
backgroundColor : '#000',
color : '#fff',
width : '200px',
padding : '20px',
});
One thing to note is that the "this" context does not change, and you can still access objects in that context.
var foo = "bar",
obj = {
hello: "world",
outside : "warm"
},
outside = "cold";
with(obj) {
hello = "javascript";
console.log(outside); // warm
console.log(this.outside); // cold
console.log(foo); // bar
}
You can see that if a property in the scoped object does not exist (foo), it'll access the outside property, this can be somewhat confusing.
The controversy
There are quite a few voices that advice against the usage of with because of it's performance implications and somewhat unpredictable behavior that code may result in. Let's take a look at some of these problems.
“It was well intentioned, but the language would be better if it didn't have it.” — Douglas Crockford
Implicit globals
Using with you cannot add new properties to an object. If you try to do this, it will instead become a global variable, which is very much undesired.
Consider the following:
with(document.getElementById('el').style) {
colors = '#fff'; //oops we mispelled this property
}
Because the property colors, does not exist we now have a new impicit global variable. Always make sure that properties exist before trying to modify them.
Performance
Optimizing with for speed is difficult and it's best avoided when used in code that go through many iterations. Take this very unscientific basic test as an example:
var obj = { hello: "world" },
value;
function without_with () {
for ( var i = 0; i < 9999; i++ ) {
value = obj.hello;
}
}
function with_with() {
with(obj) {
for ( var i = 0; i < 9999; i++ ) {
value = hello;
}
}
}
Profiling theses functions in Firebug+Firefox 3.6, we get some interesting results:
| Function | Calls | Percent | Own Time | Time | Avg | Min | Max |
|---|---|---|---|---|---|---|---|
| with_with | 10 | 91.08% | 96.549ms | 96.549ms | 9.655ms | 9.487ms | 9.919ms |
| without_with | 10 | 8.92% | 9.453ms | 9.453ms | 0.945ms | 0.931ms | 0.959ms |
As we can see, over a large set of iterations, there is a little performance hit, so using the with statement in heavy duty operations is best avoided.
I've set up a test case on jsperf so you can test performance by yourself.
Conclusion
In my opinion, as long as you are aware of the caveats of with and how it works, it's OK to use. But mostly, I think that the trade-offs are bigger than the benefits.
References and further reading
Books
- Chapter 8. With Statements - Secrets of the JavaScript Ninja by John Resig
- 6.18 with - JavaScript: The Definitive Guide by David Flanagan
- Appendix B - Bad Parts - Section B.2. with Statement - JavaScript: The Good Parts by Douglas Crockford
On the web
- Are there legitimate uses for javascripts with statement? @ stackoverflow
- with Statement Considered Harmful @ Yahoo! User Interface Blog