“With” considered harmful

Looking back over my entries in the last couple of weeks, I see that I’ve got a bit of a theme going here.  A number of entries have been about programming language features that exist in both JScript and VBScript, but have semantics just dissimilar enough to trip up the unwary.  Well, here’s another one! What’s the difference between this VBScript:

Function Rezrov(Foo)
  ' whatever
End Function
' ...
With Frobber.Blorple
  .Frotz 123, 456
  Rezrov .Cleesh
End With

and this JScript:

function Rezrov(Foo)
{
  // whatever
}
with(Frobber.Blorple)
{
  Frotz(123, 456);
  Rezrov(Cleesh);
}

?  At first glance, both look pretty straightforward.  But there is an important difference. The VBScript example is exactly equivalent to this program:

Set Temp = Frobber.Blorple
Temp.Frotz 123, 456
Rezrov Temp.Cleesh

whereas the JScript is logically equivalent to this pseudo-JScript:

var temp = Frobber.Blorple;
if (temp has property frotz)
  temp.frotz(123, 456);
else
  frotz(123, 456);
if (temp has property Rezrov)
{
  if (temp has property Cleesh)
    temp.Rezrov(temp.Cleesh);
  else
    temp.Rezrov(Cleesh);
}
else
{
  if (temp has property Cleesh)
    Rezrov(temp.Cleesh);
  else
    Rezrov(Cleesh);
}

As you can see, if Frobber.Blorple actually has a Rezrov member, these two programs do completely different things.  In the VBScript program, you know (and the compiler knows) whether you are calling a property on the With object or not.  In the JScript program, you don’t know and the compiler doesn’t know either.  The compiler has to emit code that does the namespace lookup at runtime.

This is extremely costly!  Consider for example what happens to local variables:

function foo(){
  var abc = 123;
  with(Frobber.Blorple)
  {
    Rezrov(abc);
  }
}

Is that a reference to local abc or to Frobber.Blorple.abc?  Like yesterday, because of the potential of expando objects the compiler cannot tell.  Normally in this case the compiler would generate a binding to the exact offset in the local scope table, but since the compiler cannot do that, it must instead generate a by-name lookup of the entire scope chain.

It gets worse.  Suppose you provide the Frobber.Blorple object and one day you want to add a property to it called Blah.  How can you do so safely? You can’t!  Every user of your object who put it in a with block and who has a local or global variable called Blah used in that with block is going to suddenly break.  Every time you add (or remove) a property, you run the risk of changing the variable lookup semantics of an existing program.  And the converse holds as well — every time you use a with block, you run the risk of accidentally binding to the wrong thing when someone changes the object properties.

The with block in JScript is a terrible programming construct.  It makes your programs slower, harder to understand, brittle in the face of changes and generally more bug prone.   Do not use it! 

The VBScript With block on the other hand, by the simple expedient of requiring a leading period on all references to the object, does not have these problems.

For a discussion of the pros and cons of the feature in VB, this discussion on the Fog Creek software forum has input from both me and from the designer of the feature, Joel Spolsky. https://discuss.fogcreek.com/joelonsoftware2/50125.html


Commentary from 2020:

  • I stand by my opinion that “with” in JS is harmful; it’s a bad feature that should never have been added in the first place. It is deprecated in ECMAScript 6.
  • The tradition of calling out bad programming language features by writing a post titled “X Considered Harmful” is a snowclone. I’m happy to participate in this tradition.
  • A commenter reminisced about the pleasures of playing Enchanter, Sorceror and Spellbreaker from Infocom; many of the metasyntactic names in this blog are from those games.
  • I also reinforced in the comments that the fundamental problem is not that “with” in JS makes your variable lookups hundreds of times slower — odds are good there is even slower stuff in your JS program! The problem is that it makes programs hard to read and reason about, and it makes them brittle in the face of changes; broken is much worse than slow.
  • Another commenter pointed out that the combination of automatic semicolon insertion and ignoring whitespace would make it ambiguous if the VBScript-style “dot prefix” notation were adopted in JS. Excellent point!
  • For reasons unknown to me, this article has been deleted from the MSDN archive of my original blog. Thanks to the participants in this twitter thread for finding it!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s