Posted on

What’s With ‘with’

Yesterday Richard asked me about ‘with’ in Javascript.  I said I knew of it, and what it did, and that in general the community seemed to be against using it, but for reasons unknown.  I decided to look into the subject a little, realizing I didn’t know why the community avoids it.

If you don’t know, here’s the gist of how ‘with’ works: it takes an expression and a series of statements.  It evaluates that expression to get an object, and then sets that object to be the lexical environment of the statements, which are then executed.  And then the original lexical environment is restored.

This makes ‘with’ useful to cut down on typing.  For example, this series of statements:

myReallyLongObject.foo();
myReallyLongObject.bar();
myReallyLongObject.baz();

Can be more succinctly written as:

with ( myReallyLongObject ) {
    foo();
    bar();
    baz();
}

Pretty straight-forward stuff.

Why would people be against this?  The main reason I could find is because Douglas Crockford said so, in his blog post “with Statement Considered Harmful.”  He is well-respected in the Javascriptcommunity—he created JSON, among other things—and so when Mr. Crockford says something about Javascript, people tend to listen.

But—to be frank—his argument against ‘with’ is weak. His point is as follows…

Given code like:

with ( someObject ) {
    valid = true;
    message = "Nom nom nom";
}

You have no way of knowing whether or not ‘valid’ and ‘message’ are members of ‘someObject’.  They could be global variables being referenced inside of the with-block, since that is completely legal.  You would reasonably _assume_ that they are members of that object, but there is nothing in the above code that proves that assertion.  This is why Mr. Crockford warns against using ‘with’, because of the potential for this inherent confusion.

My rebuttal:

Go look up the definition of ‘someObject’ if you don’t know which members belong to it.  I don’t consider that so difficultor unreasonable as to ban a useful language construct from common practice.  Yes—people may have monkey-patched ‘someObject’ all over the place and it may be tedious to build up a full definition, even with a powerful editing environment.  But I think we should teach programmers to be disciplined with powerful tools, instead of replace those tools with foam toys out of the fear they may hurt themselves and others.

The Javascript community, however, does not agree with me.  In fact, yesterday I sent out an email mentioning the 5th Edition of ECMA-262, defining what is essentially the next version of Javascript.  It includes the ‘with’ statement, but also a ‘strict mode’.  When strict mode is enabled, ‘with’ statements are syntax errors.  This is a very strong sign that ‘with’ will be phased out of the language in coming years.

That’s too bad, because it can be useful for aliases over a large chunk of code.  Since ‘with’ evaluates an object to set the lexical environment, and since Javascript objects are just hashes, we can do stuff like:

with ({
    User: com.cybersprocket.FooApp.User,
    DB:   com.cybersprocket.DB.Postgres
}) {
    var currentUser = new User();
    var status = DB.query(...);
    // etc
}

Which can prove useful when you have long namespaces.

The people who think the above should be done away with from the language point out that you can achieve the same thing by writing:

(function (User, DB) {
    var currentUser = new User();
    var status = DB.query(...);
    // etc
})(com.cybersprocket.FooApp.User, com.cybersprocket.DB.Postgres);

I find it much better to see up-front the definitions of ‘User’ and ‘DB’, but a significant amount of people think the above is an acceptable substitute.

There is one pitfall you need to be aware of.  Never use ‘with’ to _add_  properties to an object.  If they do not already exist, do not add them using ‘with’, because that will not have the intended effect.

Consider this code:

function User() {
    this.name;
}

var name = "Eric";
var age  = 25;

var Lobby = new User();

with (Lobby) {
    name = "Lobby"
    age  = 36;
}

print(Lobby.age);

The example User object has no ‘age’.  Because of that, when I try to add one inside of ‘with’, the global ‘age’ is blasted over.  Later when I try to print ‘Lobby.age’ that comes out as ‘undefined’ because it was never set within ‘with’.

But even with that pitfall, I have no problems with ‘with’, and hopefully have shown you how it can be useful in code.  Too bad it will probably be a relic in a few years.