11

Pretty much every resource documenting with that I can find warns against using it, mostly because if a variable is not defined it may have unpredictable effects.

I want to understand it so that I can make effective use of it - after all, it's there for a reason. Even eval has its non-evil uses!

So, with that in mind, let's say I want to delete all child nodes from an element, without using elem.innerHTML = "";

Would the following be safe?

with(elem) while(firstChild) removeChild(firstChild);

Note that at this point I'm not caring for readability, just functionality. Since firstChild is a property of, and removeChild a method of, all element nodes, it should be fine to use with this way, right?

Similarly, let's say I want to set some styles.

with(elem.style) {
    color = "red";
    backgroundColor = "black";
    fontWeight = "bold";
}

Since all of these are properties of the style object (even if not defined in the stylesheet, they are there as empty strings), it's fine to use with like this, right?

Am I missing something, or is the constant warning to not use with similar to the one against PHP's mysql extension: a protection against dumb programmers?

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • I vote for a protection against misuse – zerkms Jun 25 '13 at 08:43
  • 4
    Keep in mind that usage of the `with` statement throws an error in strict mode. So I'd say there might be more to it than just "protection" (but cannot say what right now and the spec is not specific in this point). – Felix Kling Jun 25 '13 at 08:44
  • I think the main danger of `with` is that the variables are exposed to the whole scope, but I've seen workarounds with IIFEs. Apparently it's _very_ slow when you can just cache the lookup in a variable and use that right? But that danger can be an advantage I suppose. – elclanrs Jun 25 '13 at 08:44
  • `eval` has its non-evil useS? I can think of one, that doesn't apply anymore: eval-ing JSON strings, but thanks to `JSON.parse`, we don't have to. – Elias Van Ootegem Jun 25 '13 at 09:00
  • 2
    Please search before asking: exact duplicate of [Are there legitimate uses for JavaScript's "with" statement?](http://stackoverflow.com/questions/61552/are-there-legitimate-uses-for-javascripts-with-statement). Also related: [Future of the with-statement in Javascript](http://stackoverflow.com/questions/5373577/future-of-the-with-statement-in-javascript) – Bergi Jun 25 '13 at 12:25
  • The main argument against using `with` is the relative difficulty to reason about it. – holographic-principle Jun 25 '13 at 12:29
  • @Bergi The more I read SO, the more I'm certain that *all good questions* are already asked. – VisioN Jun 25 '13 at 12:57

4 Answers4

4

The with keyword in Javascript is a bit of a throwback to when it was competing with VBScript in the late 90s. It's still there, but disabled if you 'use strict'; and considered an error by just about every Javascript validator out there.

There are two major problems with it, both related to the way scope works in Javascript:

var color = "green";
var foo = "bar";

with(elem.style) {
    color = "red";
    foo = "something else";
}

What's color now? What is foo? Not only is this confusing code, but due to how Javascript looks up variables in the with scope it's also very slow code as now every statement has an additional scope to search for each variable used.

This is one of the reasons why modern frameworks like jQuery and Prototype use chaining. They use call and apply on functions to control what this represents, meaning that you make calls to this.something rather than with(this){something}.

So to answer your question: there is one good use of with in Javascript - allowing IE4 and IE5 websites written in the 90s to still work today.

That's it. You shouldn't use it in new code.

Keith
  • 150,284
  • 78
  • 298
  • 434
1

The only safe use of with is not using it at all. There's no task that can't be accomplished without it and modern standards outright disable it in strict mode.

For all purposes it is considered a design mistake that is only preserved for backwards compatibility.

Oleg V. Volkov
  • 21,719
  • 4
  • 44
  • 68
  • How would you accomplish to write a templating engine [like this](http://documentcloud.github.io/underscore/docs/underscore.html#section-135) without `with` and similar simplicity? – Bergi Jun 25 '13 at 12:43
  • @Bergi - The Underscore template engine does not require `with`. Search the code and docs for `settings.variable`. It only uses `with` if you don't provide a `settings.variable`. – Michael Geary Jun 25 '13 at 16:18
  • Yes I know that, but with a variable the template source needs to use dot accessors over and over. If they shell be avoided, you can use `with` (and it's fine there). – Bergi Jun 25 '13 at 16:24
0

with simply puts the variable on top of the stack of "maps" searched for variables.

Normal stack is (searched top to bottom)

  • window
  • (root - just having window)

So if you have

var foo = { document: "doc.pdf" };
window.myFunc = function(){
    with( foo ){
        alert( document );
    }
}

The stack within with is

  • foo
  • window
  • (root - just having window)

It will surely print foo.document and not window.document.

In this case it's obvious that you shouldn't use document like that, as it's typically used in browsers. But ECMAScript specification doesn't define that, so in other environments, other variables may be on the stack by default (even more of them).

The danger is that whatever you call within the with statement has that on the stack too. This would fail on the document.url call:

// Some 3rd-party library
function redirect( url ){
    document.url = url;    // url is undefined in document
}

var bar = { document: "20x20" };

with( bar ){
    redirect(); //
}
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
  • The actual danger is described here: http://yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/ – Bergi Jun 25 '13 at 12:36
  • What's the difference between `root` and `window`? And your "danger" example is completely wrong; have you tried it? – Bergi Jun 25 '13 at 12:36
  • That's the same case. And the example is from a book by David Flanagan. – Ondra Žižka Jun 25 '13 at 12:42
  • If `window` *is* your `root` scope, don't put them on different bullet points to avoid confusion :-) Oh, and throw that book away. Test the code *yourself*. – Bergi Jun 25 '13 at 12:45
  • No, it's not root scope. Root scope is the scope which has `window`. Then comes `window`. See e.g. the source code of Firefox. – Ondra Žižka Jun 25 '13 at 12:47
  • Don't forget that JavaScript is not only in browsers. It's also e.g. in Thunderbird, Adobe products, etc. And `window` is not the main variable there. The root context contains other objects. – Ondra Žižka Jun 25 '13 at 12:49
  • Well, `window` is just a variable in the root scope that points to the root scope itself, isn't it? – Bergi Jun 25 '13 at 12:50
  • Not really, try `window.window.window` – Ondra Žižka Jun 25 '13 at 12:51
  • Ha! That didn't work years ago :) You got me. Perhaps they changed that to be compatible with IE which did that. – Ondra Žižka Jun 25 '13 at 21:23
0

The with statement is just a shorthand for writing recurring accesses to objects:

For

foobar.foo.bar.baz = 'bubu';
foobar.foo.bar.buz = 'baba';

you can write

with(foobar.foo.bar){
    baz = 'bubu';
    buz = 'baba';
}

Thats nice! BUT will foobar.foo.bar be modified or will the global variables baz and buz modified? In some cases it is impossible to know for sure.

JavaScript provides a better alternative

var better = foobar.foo.bar;
better.baz = 'bubu';
better.buz = 'baba';

Now there is no ambiguity.

Lars Natus
  • 183
  • 1
  • 1
  • 6