with Statement Considered Harmful

April 11, 2006 at 7:52 am by Douglas Crockford | In Development | 46 Comments

JavaScript’s with statement was intended to provide a shorthand for writing recurring accesses to objects. So instead of writing

ooo.eee.oo.ah_ah.ting.tang.walla.walla.bing = true;
ooo.eee.oo.ah_ah.ting.tang.walla.walla.bang = true;

You can write

with (ooo.eee.oo.ah_ah.ting.tang.walla.walla) {
    bing = true;
    bang = true;
}

That looks a lot nicer. Except for one thing. There is no way that you can tell by looking at the code which bing and bang will get modifed. Will ooo.eee.oo.ah_ah.ting.tang.walla.walla be modified? Or will the global variables bing and bang get clobbered? It is impossible to know for sure.

The with statement adds the members of an object to the current scope. Only if there is a bing in ooo.eee.oo.ah_ah.ting.tang.walla.walla will ooo.eee.oo.ah_ah.ting.tang.walla.walla.bing be accessed.

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.

Fortunately, JavaScript also provides a better alternative. We can simply define a var.

var o = ooo.eee.oo.ah_ah.ting.tang.walla.walla;
o.bing = true;
o.bang = true;

Now there is no ambiguity. We can have confidence that it is ooo.eee.oo.ah_ah.ting.tang.walla.walla.bing and ooo.eee.oo.ah_ah.ting.tang.walla.walla.bang that are being set, and not some hapless variables.

Share and extend: Bookmark with Yahoo! My Web | Bookmark with del.icio.us | digg it! | reddit!

46 Comments »

RSS feed for comments on this post. TrackBack URI

  1. Thanks for publishing this useful piece of advice, as it should be known by all JS developers.

    Maybe you’ll consider a similar analysis and discussion of the proper uses of the eval() function. [eval() considerations].

    Comment by Brad Fults — April 11, 2006 #

  2. I would really love syntax like


    with (ooo.eee.oo.ah_ah.ting.tang.walla.walla as o) {
    o.bing = true;
    bang = true;
    }

    so you know

    ooo.eee.oo.ah_ah.ting.tang.walla.walla.bing

    and the global variable bang are set to true.

    Just like for example the foreach construct in PHP.


    $numbers = array(1, 1, 2, 3, 5, 8, 13);
    foreach ($numbers as $number)
    print $number.' ';

    Comment by Frans — April 11, 2006 #

  3. [...] On the Yahoo! user interface blog I just saw their post “with Statement Considered Harmful“. Good advice. [...]

    Pingback by BarelyBlogging » Blog Archive » Three more things you should not do in JavaScript — April 11, 2006 #

  4. This has to be one of the most entertaining code examples ever. Thanks for sharing it in such an understandable / pallettable way.

    Comment by Nathan Smith — April 11, 2006 #

  5. I’m pretty sure the “var” solution is faster too – “with” expands the overall namespace and with JS being dynamic, the walla.walla object will have to be checked upon each variable reference to see if it has a “bang” or a “bing”, ne?

    Comment by Michael Mahemoff — April 11, 2006 #

  6. Frans,

    Your suggested syntax is not really much different than the “better alternative” suggested by Doug. Personally, I’ve forgotten that “with” even exists and just use this technique.

    And I agree, this was a fun code example…

    Jeff

    Comment by Jeff Schiller — April 12, 2006 #

  7. how about:
    function (o) {
    o.bing = true;
    bang = true;
    }(ooo.eee.oo.ah_ah.ting.tang.walla.walla);

    Comment by Ron — April 12, 2006 #

  8. [JavaScript] “With” Statement Considered Harmful…

    [via qwghlm.co.uk] Over at Yahoo’s User Interface Blog, JavaScript guru Douglas Crockford shows that using JavaScript’s with statement, a bit of syntactic sugar meant to save time and typing, isn’t such a good idea. He points out that in the code

    Trackback by Tucows Farm: The Tucows Developers' Hangout — April 13, 2006 #

  9. Too bad that Javascript doesn’t use some prefix such as “@” to specify member variables in a with statement. eg. with (ooo.eee.oo.ah_ah.ting.tang.walla.walla) {
    @bing = true;
    @bang = true;
    }

    Something along those lines would make the statement pretty useful still.

    Comment by Josh Peters — April 15, 2006 #

  10. Using sensible variable names would probably help a lot more.

    Something along the lines of:

    with(My.Utils) {
    addEvent(…);
    var z = append(x, y);
    }

    is pretty acceptable in my opinion and better than most languages import/using statements because it’s scoped.

    Comment by Daniel — April 18, 2006 #

  11. Something along the lines of:

    with(My.Utils) {
    addEvent(…);
    var z = append(x, y);
    }

    Has 16 possible interpretations. It might mean

    var w = My.Utils;
    w.addEvent(…);
    var z = append(w.x, w.y);

    or it could mean

    var w = My.Utils;
    w.addEvent(…);
    var z = w.append(x, y);

    or one of 14 others. I can’t read something along these lines and have confidence that I know what it is doing. How can I have confidence that it is correct?

    Comment by Douglas Crockford — April 20, 2006 #

  12. Because you’ve used sensible variable names….

    If you’ve got a variable called ‘My.Utils.x’ you’ve got bigger problems than the ‘with’ statment.

    Which just leaves the possibility that addEvent and append might be in either My.Utils or your current namespace. But why does that matter? Are you in the habbit of creating multiple addEvent functions with contradictory semantics? If so, I hope you’re not putting them in the global scope.

    If you think about it, the code I posted has much more than 16 variations. And will if you use ‘with’ or not.

    Comment by Daniel — April 21, 2006 #

  13. Consider also this note found at Core Javascript Reference 1.5 by Netscape:

    “Note that using a ‘with’ statement will significantly slow down your code. Do not use it when performance is critical.”

    I know things have changed and Firefox now includes a new version of JS and maybe IE doesn’t have this problem at all. But I tend to keep it in mind.

    Comment by Daniel Fernandez — April 26, 2006 #

  14. [...] Generally a shallow tree is better than a deep tree. One could imagine that YAHOO.util.Dom.get could have been factored more compactly perhaps as YAHOO.get . And perhaps someday it will, but for now YAHOO.util.Dom.get is not measurably slower, and it is helping Yahoo to manage its evolving codebase. (And if you don’t like the length, you only have to type it once. See with Statement Considered Harmful .) [...]

    Pingback by Global Domination » Yahoo! User Interface Blog — June 1, 2006 #

  15. Ron – Although your method is pretty, I don’t think it is very efficient. To execute your code, the javascript interpreter has to push something onto the call stack, where as setting a local variable works within an already existing stack.

    Comment by Taylor — June 30, 2006 #

  16. I knew that headline was familiar

    Comment by rymo — August 13, 2006 #

  17. Couple comments:
    1. Scope chaining is not something that is only introduced by the “with” statement. Closures do the same exact thing. Any time a function is declared a new scope variable is created and it is nested into or chained to the existing scope. This is basically the same thing that the with statement is doing. Confusion created by these nested scopes can certainly occur with nested closures. This is a core aspect of JavaScript, and I don’t believe it really a great reason to drop “with” usage altogether. The main reason that with statement can create more confusion is that the properties of the object that we are going to do the “with” can and often are set elsewhere, whereas the properties of the current scope are always visible in the immediate code. However, it is certainly possible that code may be structured in such a way that the properties of the “with” object are apparent in the code.
    2. Scope chaining used properly can acheive performance enhancement as well degradation. Whenever the JS machine encounters a symbol it must go through the scope chain to do a lookup. If you are referencing a global variable (or any variable not in the top scope, that is the target of the “with” statement), a deeper scope chain will take more time to do a lookup, and this is where performance degradation can be caused by the with statement. However if we consider Douglas’s example, when JS machine encounters o.bing, it must first do a lookup in the current scope for “o” and then it must do a lookup in the o object for “bing”, so it must do two lookups. However, if the with statement is used, there only needs to be one lookup, that is against the current top scope object. I believe that adding another scope on to the scope stack is not a very expensive operation. With these considerations, I believe that code that requires performance tuning (there are situations where code is used heavily enough that performance is more important than readability) may benefit from the “with” statement. Has anyone done any performance tests on the “with” statement (I am curious if my assertions are correct).

    Comment by Kris Zyp — September 26, 2006 #

  18. i = new JS_user ;D , so just a wild thought…

    What about:

    with (ooo.eee.oo.ah_ah.ting.tang.walla) {
    walla.bing = true;
    }

    or…

    with (o = ooo.eee.oo.ah_ah.ting.tang.walla.walla) {
    o.bing = true;
    }

    Comment by ec — October 31, 2006 #

  19. Why do you want to be posting stuff which is wrong?

    Comment by Douglas Crockford — October 31, 2006 #

  20. The with statement used with an object literal can be a very handy way to assign static variables with less overhead (and thus better performance) than a closure. Using an object literal and with statement in this manner does not open the door to uncertainties, and is conceptually similar to the “let” statement in Javascript 2.

    For example, if I write YAHOO.util.Dom and YAHOO.util.Event 57 times each in some program, then I can save quite a few bytes (and a lot of typing) by doing this:

    with ({
    Dom:YAHOO.util.Dom,
    Event:YAHOO.util.Event
    }) {

    // my program here…

    }

    This assigns a reference to “YAHOO.util.Dom” to the “Dom” variable, without polluting the global scope.

    Comment by Isaac Z. Schlueter — November 12, 2006 #

  21. [...] Normally, the with statement is a pretty bad idea. However, when used with an object literal, it can be a very handy way to create private variables. This: [...]

    Pingback by Schlueterica » Lessons in Javascript Optimization — January 12, 2007 #

  22. You don’t need with there, either. It is better to use vars in a function. If you want something trickier just to be tricker, try this:

    (function (Dom, Event) {

    // my program here…

    }) (YAHOO.util.Dom, YAHOO.util.Event);

    Comment by Douglas Crockford — January 12, 2007 #

  23. [...] they encourage weak coding patterns. JavaScript has more than its share of attractive nuisances (with, implied globals, new, and semicolon insertion, to name a few). Your programs will be stronger if [...]

    Pingback by I’d Rather switch Than Fight! » Yahoo! User Interface Blog — May 14, 2007 #

  24. Douglas, explain the advantage of using your suggestion of:

    (function (Dom, Event) {

    // my program here…

    }) (YAHOO.util.Dom, YAHOO.util.Event);

    over Isaac’s suggestion of:

    with ({
    Dom:YAHOO.util.Dom,
    Event:YAHOO.util.Event
    }) {

    // my program here…

    }

    They appear functionally and semantically equivalent.

    Comment by Seth Flowers — May 24, 2007 #

  25. Mr. Crockford, you propose a new snippet in your last comment, which is equivalent to :

    (function () {
    var o = …
    o.foo = …
    o.bar = …
    })();

    Thank you for posting it, I was missing the parens around the function statement when I came up with it, and without them the snippet does not work. (I wonder why, actually ?)

    However you call that snippet “tricky”. Did you mean to be disparaging, or was it irony ? Because it seems to me that it is the ideal way of implementing your suggestion from the article without polluting the global namespace with the temporary shortcut variables.

    Comment by David Holmes — June 7, 2007 #

  26. Responding to myself, to save anyone else the trouble :

    I have come to the realization that the above pattern is indeed very common. One can encounter it in the YUI code for example, and I have seen it called a “Crockford Module pattern”.

    So, tricky, but very useful !

    Comment by David Holmes — June 21, 2007 #

  27. [...] бы всё хорошо и удобно. Но вот мне на глаза попалась статья на YUI-блоге, которую и попробую [...]

    Pingback by .wpal » Blog Archives » зло with — July 3, 2007 #

  28. [...] C++, porém o estilo Pascal não é recomendado, por causa da natureza dinâmica da linguagem (with-statement-considered-harmful). Como propriedades podem ser adicionadas livremente aos objetos e variáveis globais podem ser [...]

    Pingback by O “with” em linguagens de programação « blog — July 20, 2007 #

  29. [...] with statement is another that is often recommended to avoid in JavaScript. For YUI Compressor, the reason is the same for eval(): just the presence of with in a function [...]

    Pingback by Helping the YUI Compressor » Yahoo! User Interface Blog — February 11, 2008 #

  30. This also works:


    with ({o:ooo.eee.oo.ah_ah.ting.tang.walla.walla})
    {
    o.bing = true;
    o.bang = true;
    }

    but reads a little nicer and limits the scope of ‘o’.

    Comment by Kevin Greer — May 1, 2008 #

  31. I kind of like Kevin Greer’s last addition, though here’s a variation I’ve not seen commented here yet:


    {
    var o = ooo.eee.oo.ah_ah.ting.tang.walla.walla;
    o.bing = true;
    o.bang = true;
    }

    This limits the scope of “o” while not changing “this” or restricting access to parent/global variables. Also very, very minor, but this method also involves one less object being created.

    Comment by Mark A. Ziesemer — May 12, 2008 #

  32. JavaScript does not have block scope, so Mark’s example lacks any benefit. Kevin’s example is larger and slower and wrong.

    Comment by Douglas Crockford — May 13, 2008 #

  33. Yes, I realized I was incorrect shortly after I posted. Mr. Crockford is completely correct – JavaScript does hot have block scope.

    However, I don’t see how the example referenced by Isaac, Seth, and Kevin is larger or slower, and I definitely have a difficult time realizing how it is “wrong”.

    It definitely works, as well as appearing to solve the concerns addressed in the original post.

    It additionally has the benefit of readability by declaring the variables and values before the code block, rather than after. It also pairs the variables names and assigned values together, rather than splitting the variables beforehand and the values after, making the orders significant.

    Comment by Mark A. Ziesemer — May 19, 2008 #

  34. AS A RULE, Objects must be predefined in JScript and javascript, or the code breaks altogether…

    Hence,–

    with(coconut.lime = new Object) lime=true // should work

    cf for predefined parent.object,
    with(parent) parent.object works too: as well as object works … reduplication doesn’t bother it.

    Comment by Mr. Raymond Kenneth Petry — September 22, 2008 #

  35. Hmm, I seem to remember reading about the following syntax (dunno what version/flavor of Javascript it can be used with though):

    let ovlin=object.with.very.long.irritating.name;

    ovlin.bing=”bada”;

    “let” being like “var” except it is a kind of “reference” rather than crating a new local variable. I’m unsure about the exact differences between using var and let, especially in a language such as Javascript – However, I assume it is more efficient to use let in certain situations?

    Comment by Privacy Lover — December 5, 2008 #

  36. JavaScript looks for variable names in a hierarchy manner.

    so

    var obj = { a: 5, b: 6 };
    var a = ‘a’, b = ‘b’;

    with(obj) {
    log(a + ‘ ‘ + b); // 5 6
    }

    it will check if the “parent” scope has these variables and then check it’s parent etc…

    but again it also goes back to the javascript engine. I tested it with Firefox 3

    and also again if you want to use a javascript compressor then it might be tough.
    yesterday I started using them and I found that the YUI Compressor does handle it well.

    I myself have never used with

    Comment by Samer Ziadeh — December 10, 2008 #

  37. /*
    You should Never use the with statement like this, NEVER use with(){} to asign values to an object using the = operator.

    However it works excellently as a using statement for library namespaces, for example:
    */

    //library definition:
    var mysuperawsomelib={
    somenamespace:{
    libfunk:function(){},
    dosomethingfunky:function(){}
    },
    somestring:’w00t!’
    };

    //usage:
    with(mysuperawsomelib.somenamespace){
    dosomethingfunky();
    libfunk();
    };

    //never do:
    with(mysuperawsomelib){
    somestring=’changedstring’;
    }

    /*
    because if for some reason mysuperawsomelib.somestring does not exist, window.somestring=’changedstring’; happens instead, and you now have a global variable called somestring. So Never use with(){} to assign properties to objects, Only use it like a using statement for libraries.
    */

    //This is the worst use for with(){} I’ve seen:
    with(document.getElementById(“mydiv”).style){
    position=’absolute’;
    top=234+’px’;
    left=475+’px’;
    }
    /*
    NEVER do this, because if it doesn’t have a position or top or left property already assigned, it will asign it to the global variable window, instead of mydiv.style…. It may also work differently in different browsers…
    */

    Comment by Brian Van — June 7, 2009 #

  38. [...] to YAHOO.tool so I don’t have to include the package name for each Object instance (it is considered harmful though). Now I switch over to firefox, open up the page and [...]

    Pingback by James Carr » Blog Archive » Javascript Test Driven Development With YUI Test — June 18, 2009 #

  39. Sure, with is bad if you’re doing assignments and/or don’t know about what attributes are going to be hanging around on your objects. But it has perfectly valid and safe uses. Something like:

    with (window.location) { protocol + ‘//’ + host + pathname }

    can’t possibly have any deleterious effects, and it is clearer and more expressive than other alternatives involving closures or predefined vars.

    Comment by Frederick Polgardy — June 24, 2009 #

  40. The point is definately valid in the particular “walla walla bing bang” case.

    However, here is an appropriate use of with that is perhaps not harmful.


    with( document.getElementById('foo').style ) {
    fontSize = '15px';
    fontWeight = 'bold';
    color = '#000000';
    }

    Comment by Noah — November 7, 2009 #

  41. And in reponse Brian Van 3 posts above:

    Wrong. All style objects contain every style property already defined in them, always, in all browsers, regardless of whether they have ever been set by you.
    Default values are usually blank strings, but sometimes things like “auto” or “inherit”.

    Comment by Noah — November 7, 2009 #

  42. The Fifth Edition of the ECMAScript Standard has a strict mode that does not permit the use of the with statement. It is likely that future editions will be built on top of ES5’s strict mode. So unless you are writing throw-away code, you would be wise to not us the with statement.

    Comment by Douglas Crockford — November 7, 2009 #

  43. That’s not what the with statement is for. It’s for importing a namespace, like “from library import *” in python. Each library has a single global namespace (object) where it keeps all of its functions and constants (you are putting all of your global state in a single object, aren’t you?), and you use a with statement to “import” it. Then you’re going to expect your locals of the same name as variables in that namespace to be clobbered, just as you expect that when you do an import in python or a let binding in lisp. If the with statement does disappear it will be possible to approximate it by creating a lambda and calling apply on it with the namespace as the new “this”, but that’s syntactically more baroque as you have to write function() every time.

    Comment by rabidsnail — December 15, 2009 #

  44. I’m still waiting why you think Kevin Greer’s example is wrong. It’s not any larger than your strong objection to it. Nor is it perceptibly slower in any way that should matter. The best reason for not using the with statement is ES5 as you mentioned, not because of some example such as Kevin’s. We have used that construct in small and large projects for years and never encountered a problem. We think it’s the only legitimately safe use of with, but of course it’s simply better to be a cautious programmer and not use arcane methodologies that can cause future problems.

    Comment by Henry Joe — December 23, 2009 #

  45. Another proposal could be to use a leading dot to indicate a “with” reference:

    with (ooo.eee.oo.ah_ah.ting.tang.walla.walla)
    {
    .bing = true; // ooo.eee…
    .bang = true; // ooo.eee…
    bing = false; // a global bing
    bang = false; // a global bang
    alert(.bing != bing && .bang != bang);
    }

    FYI and FWIW, this is what is used in VB/VBScript/ASP.

    Comment by Yimin — January 7, 2010 #

  46. The starting objection about global and local scope is only a problem is you don’t follow the long established programming convention of prefixing your variables to indicate their scope and intended type e.g. gsName (or gstrName) is a global variable that one intends to contain a string value, sName is the local variable. Generally, judging from most of the code published on the web, variable naming with javascript programmers is really sloppy as is their code layout and commenting.

    Comment by TobyHS — January 19, 2010 #

Leave a comment

Note: Comments are moderated for first-timers. Spam deleted.

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Hosted by Yahoo!

Copyright © 2006-2010 Yahoo! Inc. All rights reserved. Privacy Policy - Terms of Service

Powered by WordPress on Yahoo! Web Hosting.