388

Alan Storm's comments in response to my answer regarding the with statement got me thinking. I've seldom found a reason to use this particular language feature, and had never given much thought to how it might cause trouble. Now, I'm curious as to how I might make effective use of with, while avoiding its pitfalls.

Where have you found the with statement useful?

Rehan Haider
  • 893
  • 11
  • 26
Shog9
  • 156,901
  • 35
  • 231
  • 235

33 Answers33

534

Another use occurred to me today, so I searched the web excitedly and found an existing mention of it: Defining Variables inside Block Scope.

Background

JavaScript, in spite of its superficial resemblance to C and C++, does not scope variables to the block they are defined in:

var name = "Joe";
if ( true )
{
   var name = "Jack";
}
// name now contains "Jack"

Declaring a closure in a loop is a common task where this can lead to errors:

for (var i=0; i<3; ++i)
{
   var num = i;
   setTimeout(function() { alert(num); }, 10);
}

Because the for loop does not introduce a new scope, the same num - with a value of 2 - will be shared by all three functions.

A new scope: let and with

With the introduction of the let statement in ES6, it becomes easy to introduce a new scope when necessary to avoid these problems:

// variables introduced in this statement 
// are scoped to each iteration of the loop
for (let i=0; i<3; ++i)
{
   setTimeout(function() { alert(i); }, 10);
}

Or even:

for (var i=0; i<3; ++i)
{
   // variables introduced in this statement 
   // are scoped to the block containing it.
   let num = i;
   setTimeout(function() { alert(num); }, 10);
}

Until ES6 is universally available, this use remains limited to the newest browsers and developers willing to use transpilers. However, we can easily simulate this behavior using with:

for (var i=0; i<3; ++i)
{
   // object members introduced in this statement 
   // are scoped to the block following it.
   with ({num: i})
   {
      setTimeout(function() { alert(num); }, 10);
   }
}

The loop now works as intended, creating three separate variables with values from 0 to 2. Note that variables declared within the block are not scoped to it, unlike the behavior of blocks in C++ (in C, variables must be declared at the start of a block, so in a way it is similar). This behavior is actually quite similar to a let block syntax introduced in earlier versions of Mozilla browsers, but not widely adopted elsewhere.

Community
  • 1
  • 1
Shog9
  • 156,901
  • 35
  • 231
  • 235
  • 18
    Never thought of using with with a literal, seems legit. – Matt Kantor Dec 20 '08 at 05:47
  • 85
    This is really really dead on. I've never thought of playing with JavaScript's scope this way. Totally expanded new areas to my coding. I wish I could upvote 10 times! – kizzx2 Jun 22 '09 at 18:28
  • 31
    For those still opposed, one could always use a closure: `for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}` – Thomas Eding Jan 02 '10 at 17:38
  • 1
    +1 for a point I hadn't considered. Wish I could give an extra +1 for `<3` in your code. – eyelidlessness Apr 08 '10 at 17:58
  • 1
    This is a great idea, but doesn't always work on Chrome. See: http://jsbin.com/iyopu/edit – Max Shawabkeh Apr 20 '10 at 03:26
  • 4
    Actually, the problem linked above appears on most non-Mozilla browsers (Chrome, Safari, Opera, IE). – Max Shawabkeh Apr 20 '10 at 03:43
  • @Max: yeah, Firefox's JavaScript is converting the declaration in the second example into an expression (see: https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope#section_14 - *"A function declaration ceases to be one when it either..."*). Apparently, other JS engines don't properly capture the context when this occurs. Since it's an expression and not a declaration that's needed, the syntax in the first example should be used anyway. – Shog9 Apr 20 '10 at 15:08
  • 1
    The point is that this post might suggest that a `with({...}) {...}` is a general-purpose replacement for the anonymous closure pattern used to hide local vars. However, an anonymous closure properly captures the context on all browsers, unlike `with`. – Max Shawabkeh Apr 20 '10 at 15:14
  • Uh, @Max? [An anonymous closure hides the definition completely](http://jsbin.com/iyopu/4/edit). Whichever technique you use (`with` or an anonymous function), you'll have to make sure that the result of your function *expression* is available to the outer scope... assuming that's what you really wanted. – Shog9 Apr 20 '10 at 16:47
  • True, but with `with` the function declaration doesn't work even if it's called inside the scope: http://jsbin.com/iyopu/5/edit – Max Shawabkeh Apr 21 '10 at 01:05
  • @Max: see the last paragraph in this answer: `with` and `let` scope only those symbols defined in their argument list; JS doesn't have block scope, apart from that provided by function declarations. Chrome, etc. fail to respect the scope introduced by `with` when converting a function declaration into a function expression; once that error is introduced, it doesn't matter where the function is *called* from. – Shog9 Apr 21 '10 at 05:24
  • 1
    In my last example, "x" is in the argument list of both the function expression closure and the `with` statement. But in one the scope is respected and properly nested, and in the other it isn't. I understand *why* the problem occurs. I'm just saying that it should be clarified that a `with` does not introduce a proper scope and can not completely replace the anonymous closure pattern. – Max Shawabkeh Apr 21 '10 at 06:22
  • @Max: And again, that's a bug in Chrome that occurs when you rely on the implicit conversion of a declaration to an expression (explicit expressions do work). That said, of course `with` is not a replacement for function closures. The means by which it introduces scope is *considerably* more limited, and of course it doesn't offer any of the other features of functions. On the other hand, `with` doesn't mask an existing context object (`this`) and offers a simpler syntax. So for simple scope needs such as those I describe above, it could be useful; but as a replacement for functions, hardly! – Shog9 Apr 21 '10 at 13:00
  • 1
    @Max you can make that work by using the function expression instead http://jsbin.com/iyopu/7/edit – Jiaaro Jun 16 '10 at 14:32
  • 27
    *let* statement support in IE would really save my bacon right now, I'm struggling with my conscience on whether or not to use *with* instead. The real problem is that even with a *with* as a *let*, extra care still has to be taken because of inherited properties of an object on the prototype chain. For example, `var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };`. In the scope of the *with* statement, *toString()* is an inherited property of *Object*, so the explicitly defined function isn't called. Still a great answer, though :-) – Andy E Oct 11 '10 at 09:20
  • you don't even need to use a closure. You can pass the argument to the `setTimeout` function : ` for(var i=0;i<3;++i)setTimeout(function(num) {alert (num); },10,i);` – gion_13 Oct 12 '11 at 09:40
  • 1
    @goin: IE doesn't allow arguments to be passed in that way. IE allows you to specify the scripting language in the 3rd argument for `setInterval` and `setTimeout`, any additional arguments after that are ignored. – Andy E Oct 12 '11 at 10:18
  • JavaScript 1.7 and the let keyword are not supported by all current browsers including chrome. see the [chart](http://en.wikipedia.org/wiki/JavaScript#Versions) – MarkDav.is May 04 '12 at 15:07
  • 1
    if inside your with statement, you have a line `a=5`, where is that going to end up? The anonymous object? A global variable? Depends on whether there is a `var a ` in the function? You can get really fancy, but in the end the problems with writes makes me never want to touch it. – Ruan Mendes Jun 01 '12 at 22:16
  • This is amazing, had never considered using with like this at all but it makes a ton of sense and is incredibly useful! – Marcus Stade Dec 20 '12 at 23:52
  • 2
    scope blocking is coming up in JavaScript Harmony. –  Jun 10 '13 at 16:16
  • 1
    Not gonna hold my breath, but... Will be nice if it happens. – Shog9 Jun 10 '13 at 16:17
  • fyi I believe 'let' used to create its own enclosing block is now deprecated – Ben Taliadoros Jul 29 '15 at 10:18
  • 1
    Yeah, much has changed in 7 years, @Ben - I've added some notes to hopefully reduce any confusion this might cause. Depressing how out-of-reach this all is for production use even this many years later. – Shog9 Jul 29 '15 at 17:06
  • This makes me want them to keep the with statement because despite it reportedly bad performance, the only alternative in situations like this is evaling a string of your code which has even worse performance –  Apr 22 '17 at 23:55
165

I have been using the with statement as a simple form of scoped import. Let's say you have a markup builder of some sort. Rather than writing:

markupbuilder.div(
  markupbuilder.p('Hi! I am a paragraph!',
    markupbuilder.span('I am a span inside a paragraph')
  )
)

You could instead write:

with(markupbuilder){
  div(
    p('Hi! I am a paragraph!',
      span('I am a span inside a paragraph')
    )
  )
}

For this use case, I am not doing any assignment, so I don't have the ambiguity problem associated with that.

airportyh
  • 21,948
  • 13
  • 58
  • 72
  • 6
    That's how I've seen it used in VB. (And the only use I was aware of.) – Mateen Ulhaq Jun 18 '11 at 03:44
  • 2
    A downside to this would be that if you reference a variable within the with block which is outside the markupbuilder object, the js engine will first search for it inside markupbuilder anyway, reducing performance. – adsy Nov 05 '12 at 14:35
  • 3
    This really helps cut down on code for those working with canvas paths. – Brian McCutchon Aug 16 '13 at 03:59
  • I had a use-case very similar to this one. I needed a lot of mathematical operations. This: `y = Math.root(Math.pow(Match.cos(Math.PI), Math.sin(x)))` becomes: `with (Math) { y = root(pow(cos(PI), sin(x))); }` _(that's bullocks math of course, but to illustrate)_ – asontu Feb 06 '15 at 09:30
  • 7
    The "with" version of that code literally runs over **240 times** slower on my machine than the "non-with" version of the same. **THAT is why people say there's no legitimate use for it.** Not because it can't make code prettier in some spots. See benchmark: http://jsfiddle.net/sc46eeyn/ – Jimbo Jonny Oct 26 '15 at 17:26
  • 1
    @McBrainy - That's exactly the type of place you shouldn't be using code that runs multitude slower (see the comment I just made above this one). If you need shortcuts for super repeated code you can declare them. For example if using `context.bezierCurveTo` a hundred times straight you can say `var bc2 = context.bezierCurveTo;` and then just go `bc2(x,x,etc);` each time you want to call it. That is quite fast and even less verbose, while `with` is super slow. – Jimbo Jonny Oct 26 '15 at 17:32
  • @JimboJonny chicken & egg problem... I think people found no legitimate use for it. It only led to confusion in the past. And THAT is why JS engines like V8 have no optimizations for it. – justGoscha Sep 16 '16 at 07:46
  • @JustGoscha - Actually it's an intrinsic issue with the fact that things inside a `with` statement could be referencing different things depending on what variables/properties exist outside the `with` statement, and that makes it very hard to optimize. – Jimbo Jonny Oct 09 '16 at 18:04
  • @JimboJonny assigning a function to a variable has implications -- if the _method_ accesses properties of the object it is called on, which is implied to be the object specified to the left of the dot-accessor (`.`) operator, calling it through the assigned variable will produce runtime errors because -- and you may know this yourself without having mentioned this -- in JavaScript a method reference is decoupled from the object and `this` is unbound before the call and only specified for the call. All this said, `const bc2 = context.bezierCurveTo.bind(context);` does the trick, technically. – Armen Michaeli Apr 19 '23 at 08:30
87

As my previous comments indicated, I don't think you can use with safely no matter how tempting it might be in any given situation. Since the issue isn't directly covered here, I'll repeat it. Consider the following code

user = {};
someFunctionThatDoesStuffToUser(user);
someOtherFunction(user);

with(user){
    name = 'Bob';
    age  = 20;
}

Without carefully investigating those function calls, there's no way to tell what the state of your program will be after this code runs. If user.name was already set, it will now be Bob. If it wasn't set, the global name will be initialized or changed to Bob and the user object will remain without a name property.

Bugs happen. If you use with you will eventually do this and increase the chances your program will fail. Worse, you may encounter working code that sets a global in the with block, either deliberately or through the author not knowing about this quirk of the construct. It's a lot like encountering fall through on a switch, you have no idea if the author intended this and there's no way to know if "fixing" the code will introduce a regression.

Modern programming languages are chocked full of features. Some features, after years of use, are discovered to be bad, and should be avoided. Javascript's with is one of them.

jakub.g
  • 38,512
  • 12
  • 92
  • 130
Alana Storm
  • 164,128
  • 91
  • 395
  • 599
  • 18
    This problem only surfaces when you are assigning values to attribute of the object. But what if you are using it only to read values? I contend that it's okay to use it in that case. – airportyh Sep 22 '09 at 19:33
  • 10
    The same problem applies to reading the values Toby. In the above code snippet you don't know if name is is set on the user object, so you wouldn't know if you were reading the global name, or the user name. – Alana Storm Sep 22 '09 at 19:38
  • 13
    With reading values there's a clear precedence rule: attributes on the object are checked before variables outside the scope. This isn't any different from variables scoping in functions. The real problem with assignment and 'with', as I understand it, lies in the fact that whether or not the attribute assignment occurs depends on whether the attribute exists on the current object in question, which is a runtime property and cannot be deduced easily by looking at the code. – airportyh Sep 22 '09 at 19:46
  • 2
    I think you may be right there Toby. The write problem is enough for me to shy away from the construct entirely. – Alana Storm Sep 23 '09 at 16:40
  • _"It's a lot like encountering fall through on a switch, you have no idea..."_ -- So then let's ban switch(), too? ;-p – Sz. Mar 13 '14 at 09:18
71

I actually found the with statement to be incredibly useful recently. This technique never really occurred to me until I started my current project - a command line console written in JavaScript. I was trying to emulate the Firebug/WebKit console APIs where special commands can be entered into the console but they don't override any variables in the global scope. I thought of this when trying to overcome a problem I mentioned in the comments to Shog9's excellent answer.

To achieve this effect, I used two with statements to "layer" a scope behind the global scope:

with (consoleCommands) {
    with (window) {
        eval(expression); 
    }
}

The great thing about this technique is that, aside from the performance disadvantages, it doesn't suffer the usual fears of the with statement, because we're evaluating in the global scope anyway - there's no danger of variables outside our pseudo-scope from being modified.

I was inspired to post this answer when, to my surprise, I managed to find the same technique used elsewhere - the Chromium source code!

InjectedScript._evaluateOn = function(evalFunction, object, expression) {
    InjectedScript._ensureCommandLineAPIInstalled();
    // Surround the expression in with statements to inject our command line API so that
    // the window object properties still take more precedent than our API functions.
    expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }";
    return evalFunction.call(object, expression);
}

EDIT: Just checked the Firebug source, they chain 4 with statements together for even more layers. Crazy!

const evalScript = "with (__win__.__scope__.vars) { with (__win__.__scope__.api) { with (__win__.__scope__.userVars) { with (__win__) {" +
    "try {" +
        "__win__.__scope__.callback(eval(__win__.__scope__.expr));" +
    "} catch (exc) {" +
        "__win__.__scope__.callback(exc, true);" +
    "}" +
"}}}}";
Community
  • 1
  • 1
Andy E
  • 338,112
  • 86
  • 474
  • 445
  • 2
    but worrying that ecmascript5 stops you from doing this. Is there an ecmascript 5 solution? – kybernetikos Mar 04 '12 at 17:43
  • @Adam: I'm not sure about that. ES5 only throws an error for this in strict mode, so it's not an immediate issue if you don't have strict mode declared globally. ES Harmony might pose a bigger problem, but it might be fixable with some of the newer stuff like proxies. – Andy E Mar 04 '12 at 17:54
  • @AndyE sorry this is off topic, but is your 'command line console written in JavaScript' available anywhere? – kybernetikos Mar 05 '12 at 18:03
  • @Adam: no, it's not. The whole thing was intended to be a set of developer tools for Windows Desktop Gadgets, but I never finished it (although, the console works very well). I might finish it at some point, even though WDGs don't have a very bright future at the moment. – Andy E Mar 06 '12 at 10:07
  • @AndyE I came with something similar for the same reason too. Now WDG area dead :( – Vitim.us Jun 22 '13 at 20:33
  • 3
    Few weeks ago we moved our console implementation in Chrome from with block to some symbol magic because with block blocked some ES6 features :) – Alexey Kozyatinskiy Mar 12 '16 at 07:59
  • 1
    @AlexeyKozyatinskiy what kind of Symbol magic were you referring to? 5 years ago... :) – ADJenks Feb 10 '21 at 18:19
58

Yes, yes and yes. There is a very legitimate use. Watch:

with (document.getElementById("blah").style) {
    background = "black";
    color = "blue";
    border = "1px solid green";
}

Basically any other DOM or CSS hooks are fantastic uses of with. It's not like "CloneNode" will be undefined and go back to the global scope unless you went out of your way and decided to make it possible.

Crockford's speed complaint is that a new context is created by with. Contexts are generally expensive. I agree. But if you just created a div and don't have some framework on hand for setting your css and need to set up 15 or so CSS properties by hand, then creating a context will probably be cheaper then variable creation and 15 dereferences:

var element = document.createElement("div"),
    elementStyle = element.style;

elementStyle.fontWeight = "bold";
elementStyle.fontSize = "1.5em";
elementStyle.color = "#55d";
elementStyle.marginLeft = "2px";

etc...

  • 5
    +1 as I also think there are plenty of legitimate uses of `with`. However, in this particular case you could just do: `element.style.cssText="background: black ; color: blue ; border: 1px solid green"` – GetFree Aug 20 '10 at 08:10
  • 5
    You could achieve the same thing in one line using the simple `extend` method from either jQuery or Underscore.js: `$.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'})`. – Trevor Burnham Jun 15 '11 at 16:09
  • 11
    @TrevorBurnham - If you're going to assume jQuery is available, you'd just use its `.css()` method... – nnnnnn Sep 13 '12 at 05:15
  • 5
    What exactly is preventing these variables from going to global scope? Is it just because all CSS styles are always defined on all elements, or what? – mpen Dec 11 '12 at 16:57
  • 2
    @Mark yes, they are always defined, with nulls or empty strings as values if there is no custom style for a property – Esailija Apr 24 '13 at 19:14
  • 1
    Please substantiate the claim that the latter example is slower. – Steven Lu Oct 24 '13 at 19:46
  • 2
    @GetFree But if it's not a brand new element, you couldn't just set cssText, necessarily. – Scimonster Feb 28 '15 at 21:46
35

You can define a small helper function to provide the benefits of with without the ambiguity:

var with_ = function (obj, func) { func (obj); };

with_ (object_name_here, function (_)
{
    _.a = "foo";
    _.b = "bar";
});
John Millikin
  • 197,344
  • 39
  • 212
  • 226
  • 9
    OMG, MY HEAD EXPLODED! _without_ the ambiguity? Gotta vote that up, man! – Jarrod Dixon Feb 13 '09 at 04:32
  • @Jarrod: what is so funny about this (http://is.gd/ktoZ)? Most everyone who uses this site is smarter than me, so forgive me if I am wrong, but this seems like bad information. – raven Feb 22 '09 at 22:32
  • 14
    But that's just longer and harder to understand way of doing: var _ = obj_name_here; _.a="foo"; _.b="bar; – Rene Saarsoo Mar 09 '09 at 10:04
  • 4
    Rene: With that, you will expose the "_" variable to the outer scope, resulting in potential bugs. It will also expose any temporary variables used in calculating object parameters. – John Millikin Mar 10 '09 at 04:59
  • 30
    You'll get more bugs from `with_` being a muddy doubled version of `(function(_){ _.a="foo"; })(object_here);` (the standard way to simulate c/java-style blocks). Use that instead. – mk. Jan 30 '10 at 22:06
26

Hardly seems worth it since you can do the following:

var o = incrediblyLongObjectNameThatNoOneWouldUse;
o.name = "Bob";
o.age = "50";
Allain Lalonde
  • 91,574
  • 70
  • 187
  • 238
19

I don't ever use with, don't see a reason to, and don't recommend it.

The problem with with is that it prevents numerous lexical optimizations an ECMAScript implementation can perform. Given the rise of fast JIT-based engines, this issue will probably become even more important in the near future.

It might look like with allows for cleaner constructs (when, say, introducing a new scope instead of a common anonymous function wrapper or replacing verbose aliasing), but it's really not worth it. Besides a decreased performance, there's always a danger of assigning to a property of a wrong object (when property is not found on an object in injected scope) and perhaps erroneously introducing global variables. IIRC, latter issue is the one that motivated Crockford to recommend to avoid with.

kangax
  • 38,898
  • 13
  • 99
  • 135
  • 6
    The performance bogeyman gets trotted out frequently, almost as often as the globals thing... Always strikes me as odd, given that it's *JavaScript* we're talking about. You'd assume that the performance hit is *truly dramatic* to warrant that much attention, but... If you have any hard numbers on the cost of `with(){}` constructs like those given in other answers here, in modern browsers, I'd love to see them! – Shog9 Sep 23 '09 at 05:02
  • 6
    Why is it odd in context of Javascript? :) And yes, it is dramatic. Think about it - an implementation needs to evaluate an expression in parenthesis, convert it to object, insert it into the front of the current scope chain, evaluate statement inside the block, then restore scope chain back to normal. That's a *lot* of work. Much more than a simple property lookup that can be turned into a highly-optimized low level code. Here's a very simple benchmark I just made (let me know if you find any mistakes) demonstrating the difference - https://gist.github.com/c36ea485926806020024 – kangax Sep 23 '09 at 05:50
  • 5
    @kangax: I come from a C++ background, where it's sort of traditional for many programmers to obsess about small efficiencies in their code, even when they don't actually have a noticeable effect on the performance of the larger routine or program. It seems odd to me in the context of JavaScript, where such a large part of a routine's performance can depend on the VM implementation. I've seen a few instances where JS programmers will avoid, say, an anonymous function due to concerns over the setup cost, but this seems to be the exception not the rule, reserved for very sensitive areas of code. – Shog9 Sep 27 '09 at 15:40
  • 5
    That said, you're absolutely correct with regard to the cost of `with(){}`: setting up a new scope with `with` is hugely expensive on every browser I tested. You'd want to avoid this in any code called very frequently. In addition, Chrome exhibited a dramatic hit for any code executing within a `with()` scope. Interestingly, IE had the best performance characteristics for code within `with()` blocks: factoring out the setup cost, `with()` provides the fastest means of member access in IE6 and IE8 VMs (though these VMs are the slowest overall). Good stuff, thanks... – Shog9 Sep 27 '09 at 15:47
  • 5
    FWIW: here's the same set of tests with setup costs factored out: http://jsbin.com/imidu/edit Variable access for `with()` is almost an order of magnitude slower in Chrome, and over twice as fast in IE...! – Shog9 Sep 27 '09 at 16:01
  • @kangax - Isn't that a bit short-sighted of you? @Shog9 - Thankfully, we're talking about a cleaner and more flexible language, javascript, rather than an ugly "high performance" language, C++. Now, before you try any retorts, know that I've used C++ for a while, and I'm not exactly novice. – Christian Sep 06 '10 at 10:06
  • @Shog9: Chrome's speed drop - I think - is because V8 drops directly to interpretation from JIT-compiling when it reaches a certain level of complexity in scope etc., like when `eval()`'ing things. Or, in this case obviously, when using `with()` – Sune Rasmussen May 24 '12 at 14:05
15

Visual Basic.NET has a similar With statement. One of the more common ways I use it is to quickly set a number of properties. Instead of:

someObject.Foo = ''
someObject.Bar = ''
someObject.Baz = ''

, I can write:

With someObject
    .Foo = ''
    .Bar = ''
    .Baz = ''
End With

This isn't just a matter of laziness. It also makes for much more readable code. And unlike JavaScript, it does not suffer from ambiguity, as you have to prefix everything affected by the statement with a . (dot). So, the following two are clearly distinct:

With someObject
    .Foo = ''
End With

vs.

With someObject
    Foo = ''
End With

The former is someObject.Foo; the latter is Foo in the scope outside someObject.

I find that JavaScript's lack of distinction makes it far less useful than Visual Basic's variant, as the risk of ambiguity is too high. Other than that, with is still a powerful idea that can make for better readability.

Sören Kuklau
  • 19,454
  • 7
  • 52
  • 86
8

You can use with to introduce the contents of an object as local variables to a block, like it's being done with this small template engine.

Jordão
  • 55,340
  • 13
  • 112
  • 144
8

I think the obvious use is as a shortcut. If you're e.g. initializing an object you simply save typing a lot of "ObjectName." Kind of like lisp's "with-slots" which lets you write

(with-slots (foo bar) objectname
   "some code that accesses foo and bar"

which is the same as writing

"some code that accesses (slot-value objectname 'foo) and (slot-value objectname 'bar)""

It's more obvious why this is a shortcut then when your language allows "Objectname.foo" but still.

Sarien
  • 6,647
  • 6
  • 35
  • 55
  • 2
    great to see lisp code! I think "with" in javascript is obviously inspired by it's scheme roots as a language, but alas your getting downvoted for posting LISP on a javascript question. – Fire Crow Apr 08 '10 at 17:39
  • 2
    Upvoting on basic principle. 'with' is a phenomenally powerful construct. But most JS people don't understand closures, and write ridiculously complex Java class inheritance systems on top of JS--so how could they be aware of the metaprogramming potential that 'with' offers? – jared Sep 17 '10 at 17:14
  • Of course `with-slots` requires you to specify which slots you're using, whereas `with` will use whatever slots happen to be bound at runtime. – Samuel Edwin Ward Feb 21 '15 at 01:41
7

Using "with" can make your code more dry.

Consider the following code:

var photo = document.getElementById('photo');
photo.style.position = 'absolute';
photo.style.left = '10px';
photo.style.top = '10px';

You can dry it to the following:

with(document.getElementById('photo').style) {
  position = 'absolute';
  left = '10px';
  top = '10px';
}

I guess it depends whether you have a preference for legibility or expressiveness.

The first example is more legible and probably recommended for most code. But most code is pretty tame anyway. The second one is a bit more obscure but uses the expressive nature of the language to cut down on code size and superfluous variables.

I imagine people who like Java or C# would choose the first way (object.member) and those who prefer Ruby or Python would choose the latter.

Jonah
  • 79
  • 1
  • 1
  • Whoops I didn't realize someone already posted basically this same example a year ago. Performance issues aside, "with" makes for nice DRY code at the expense of being a bit more difficult to read. I think that for collaborations with other developers or most production code, it's good practice to avoid the "with" keyword. But if you are working with expert-level programmers and understand how to avoid potential inefficiencies then by all means go to town with "with." – Jonah Jun 26 '10 at 07:48
6

Having experience with Delphi, I would say that using with should be a last-resort size optimization, possibly performed by some kind of javascript minimizer algorithm with access to static code analysis to verify its safety.

The scoping problems you can get into with liberal use of the with statement can be a royal pain in the a** and I wouldn't want anyone to experience a debugging session to figure out what the he.. is going on in your code, only to find out that it captured an object member or the wrong local variable, instead of your global or outer scope variable which you intended.

The VB with statement is better, in that it needs the dots to disambiguate the scoping, but the Delphi with statement is a loaded gun with a hairtrigger, and it looks to me as though the javascript one is similar enough to warrant the same warning.

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • 5
    The javascript with statement is worse than the Delphi one. In Delphi, with performs just as fast (if not faster) than object.member notation. In javascript, with has to walk the scope to check for matching members, thus always making it slower than object.member notation. – Martijn Jun 05 '09 at 13:56
5

Using with is not recommended, and is forbidden in ECMAScript 5 strict mode. The recommended alternative is to assign the object whose properties you want to access to a temporary variable.

Source: Mozilla.org

ianaz
  • 2,490
  • 3
  • 28
  • 37
4

The with statement can be used to decrease the code size or for private class members, example:

// demo class framework
var Class= function(name, o) {
   var c=function(){};
   if( o.hasOwnProperty("constructor") ) {
       c= o.constructor;
   }
   delete o["constructor"];
   delete o["prototype"];
   c.prototype= {};
   for( var k in o ) c.prototype[k]= o[k];
   c.scope= Class.scope;
   c.scope.Class= c;
   c.Name= name;
   return c;
}
Class.newScope= function() {
    Class.scope= {};
    Class.scope.Scope= Class.scope;
    return Class.scope;
}

// create a new class
with( Class.newScope() ) {
   window.Foo= Class("Foo",{
      test: function() {
          alert( Class.Name );
      }
   });
}
(new Foo()).test();

The with-statement is very usefull if you want to modify the scope, what is necessary for having your own global scope that you can manipulate at runtime. You can put constants on it or certain helper functions often used like e.g. "toUpper", "toLower" or "isNumber", "clipNumber" aso..

About the bad performance I read that often: Scoping a function won't have any impact on the performance, in fact in my FF a scoped function runs faster then an unscoped:

var o={x: 5},r, fnRAW= function(a,b){ return a*b; }, fnScoped, s, e, i;
with( o ) {
    fnScoped= function(a,b){ return a*b; };
}

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnRAW(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnScoped(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

So in the above mentioned way used the with-statement has no negative effect on performance, but a good one as it deceases the code size, what impacts the memory usage on mobile devices.

alex
  • 49
  • 1
  • 2
3

For some short code pieces, I would like to use the trigonometric functions like sin, cos etc. in degree mode instead of in radiant mode. For this purpose, I use an AngularDegreeobject:

AngularDegree = new function() {
this.CONV = Math.PI / 180;
this.sin = function(x) { return Math.sin( x * this.CONV ) };
this.cos = function(x) { return Math.cos( x * this.CONV ) };
this.tan = function(x) { return Math.tan( x * this.CONV ) };
this.asin = function(x) { return Math.asin( x ) / this.CONV };
this.acos = function(x) { return Math.acos( x ) / this.CONV };
this.atan = function(x) { return Math.atan( x ) / this.CONV };
this.atan2 = function(x,y) { return Math.atan2(x,y) / this.CONV };
};

Then I can use the trigonometric functions in degree mode without further language noise in a with block:

function getAzimut(pol,pos) {
  ...
  var d = pos.lon - pol.lon;
  with(AngularDegree) {
    var z = atan2( sin(d), cos(pol.lat)*tan(pos.lat) - sin(pol.lat)*cos(d) );
    return z;
    }
  }

This means: I use an object as a collection of functions, which I enable in a limited code region for direct access. I find this useful.

rplantiko
  • 2,698
  • 1
  • 22
  • 21
  • its not a nice idea to use `with` statement this way, it just makes code hard to read because you don't know which function is global and which function is called in the scope of with object, so if any function by anyhow not defined in the object scope then it will try to access it in the global namespace – Saket Patel Apr 09 '12 at 19:15
  • Being aware of the scoping problem, I still find this useful. A mathematician reading the code wants to see directly that the above formula is an application of the "law of the 4 successive parts" in spherical trigonometry. The strict alternative obfuscates the formula: `z = Math.atan2( Math.sin(d * Math.PI / 180), Math.cos( pol.lat * Math.PI / 180) * Math.tan( pos.lat * Math.PI / 180 ) - Math.sin( pol.lat * Math.PI / 180 ) * Math.cos( d * Math.PI / 180) ) * 180 / Math.PI;` would give the same result, but it's the horror. – rplantiko Apr 10 '12 at 17:40
  • @rplantiko The thing to keep in mind is that most people do not feel comfortable with it. So unless you're writing code that no one else will ever touch. Also, I'm pretty sure I can show you a couple of usages of `with` that would stump you. – Ruan Mendes Jun 01 '12 at 21:52
3

It's good for putting code that runs in a relatively complicated environment into a container: I use it to make a local binding for "window" and such to run code meant for a web browser.

aredridel
  • 1,532
  • 14
  • 19
3

I think the object literal use is interesting, like a drop-in replacement for using a closure

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       (function(info)
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       })(data[i]);
}

or the with statement equivilent of a closure

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       with({info: data[i]})
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       }        
}

I think the real risk is accidently minipulating variables that are not part of the with statement, which is why I like the object literal being passed into with, you can see exactly what it will be in the added context in the code.

Fire Crow
  • 7,499
  • 4
  • 36
  • 35
3

I created a "merge" function which eliminates some of this ambiguity with the with statement:

if (typeof Object.merge !== 'function') {
    Object.merge = function (o1, o2) { // Function to merge all of the properties from one object into another
        for(var i in o2) { o1[i] = o2[i]; }
        return o1;
    };
}

I can use it similarly to with, but I can know it won't affect any scope which I don't intend for it to affect.

Usage:

var eDiv = document.createElement("div");
var eHeader = Object.merge(eDiv.cloneNode(false), {className: "header", onclick: function(){ alert("Click!"); }});
function NewObj() {
    Object.merge(this, {size: 4096, initDate: new Date()});
}
palswim
  • 11,856
  • 6
  • 53
  • 77
3

Using with also makes your code slower in many implementation, as everything now gets wrapped in an extra scope for lookup. There's no legitimate reason for using with in JavaScript.

Svend
  • 7,916
  • 3
  • 30
  • 45
  • 5
    Premature optimization. Don't claim "slower" unless you crunched the numbers; any overhead is likely trivial on both modern and ancient js impls. – mk. Jan 30 '10 at 22:11
  • 2
    I strongly disagree with your conclusion, based on what may not be an issue for developers other than you. – Dave Van den Eynde Mar 12 '10 at 07:34
  • 4
    @mk: ok, number crunching for you here: `var obj={a:0,b:0,c:0};var d=+new Date;with(obj){for(var i=0;i<1000000;++i){a+=1;b+=1;c+=1}}+new Date-d;` gives on average 2500, while `var obj={a:0,b:0,c:0};var d=+new Date;for(var i=0;i<1000000;++i){obj.a+=1;obj.b+=1;obj.c+=1}+new Date-d;` gives on average 750, making the one using while over 3 times slower. – yorick Jan 23 '11 at 17:24
  • 3
    Just ran these in Chrome 23 in the console when I saw this. The results I got were 1138 for the `with` code and 903 without. With this tiny difference even in a tight loop I would make an selection based on coding simplicity and ease of refactoring on a case-by-case basis before worrying about performance. – Plynx Jan 06 '13 at 12:20
3

I think the with-statement can come in handy when converting a template language into JavaScript. For example JST in base2, but I've seen it more often.

I agree one can program this without the with-statement. But because it doesn't give any problems it is a legitimate use.

Allain Lalonde
  • 91,574
  • 70
  • 187
  • 238
doekman
  • 18,750
  • 20
  • 65
  • 86
2

I think that the usefulness of with can be dependent on how well your code is written. For example, if you're writing code that appears like this:

var sHeader = object.data.header.toString();
var sContent = object.data.content.toString();
var sFooter = object.data.footer.toString();

then you could argue that with will improve the readability of the code by doing this:

var sHeader = null, sContent = null, sFooter = null;
with(object.data) {
    sHeader = header.toString();
    sContent = content.toString();
    sFooter = content.toString();
}

Conversely, it could be argued that you're violating the Law of Demeter, but, then again, maybe not. I digress =).

Above all else, know that Douglas Crockford recommends not using with. I urge you to check out his blog post regarding with and its alternatives here.

Tom
  • 15,527
  • 5
  • 48
  • 62
  • Thanks for the response, Tom. I've read Crockford's recommendation, and while it makes sense it only goes so far. I'm coming around to the idea - touched on indirectly by doekman - that the real power of with{} is in the way it can be used to manipulate scope... – Shog9 Oct 07 '08 at 00:18
2

I just really don't see how using the with is any more readable than just typing object.member. I don't think it's any less readable, but I don't think it's any more readable either.

Like lassevk said, I can definitely see how using with would be more error prone than just using the very explicit "object.member" syntax.

17 of 26
  • 27,121
  • 13
  • 66
  • 85
1

You got to see the validation of a form in javascript at W3schools http://www.w3schools.com/js/js_form_validation.asp where the object form is "scanned" through to find an input with name 'email'

But i've modified it to get from ANY form all the fields validate as not empty, regardless of the name or quantity of field in a form. Well i've tested only text-fields.

But the with() made things simpler. Here's the code:

function validate_required(field)
{
with (field)
  {
  if (value==null||value=="")
    {
    alert('All fields are mandtory');return false;
    }
  else
    {
    return true;
    }
  }
}

function validate_form(thisform)
{
with (thisform)
  {
    for(fiie in elements){
        if (validate_required(elements[fiie])==false){
            elements[fiie].focus();
            elements[fiie].style.border='1px solid red';
            return false;
        } else {elements[fiie].style.border='1px solid #7F9DB9';}
    }

  }
  return false;
}
1

My

switch(e.type) {
    case gapi.drive.realtime.ErrorType.TOKEN_REFRESH_REQUIRED: blah
    case gapi.drive.realtime.ErrorType.CLIENT_ERROR: blah
    case gapi.drive.realtime.ErrorType.NOT_FOUND: blah
}

boils down to

with(gapi.drive.realtime.ErrorType) {switch(e.type) {
    case TOKEN_REFRESH_REQUIRED: blah
    case CLIENT_ERROR: blah
    case NOT_FOUND: blah
}}

Can you trust so low-quality code? No, we see that it was made absolutely unreadable. This example undeniably proves that there is no need for with-statement, if I am taking readability right ;)

Little Alien
  • 1
  • 8
  • 22
1

CoffeeScript's Coco fork has a with keyword, but it simply sets this (also writable as @ in CoffeeScript/Coco) to the target object within the block. This removes ambiguity and achieves ES5 strict mode compliance:

with long.object.reference
  @a = 'foo'
  bar = @b
Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196
1

using "with" statement with proxy objects

I recently want to write a plugin for babel that enables macros. I would like to have a separate variable namespace that keeps my macro variables, and I can run my macro codes in that space. Also, I want to detect new variables that are defined in the macro codes(Because they are new macros).

First, I choose the vm module, but I found global variables in the vm module like Array, Object, etc. are different from the main program, and I cant implement module and require that be fully compatible with that global objects(Because I cant reconstruct the core modules). In the end, I find the "with" statement.

const runInContext = function(code, context) {
    context.global = context;
    const proxyOfContext = new Proxy(context, { has: () => true });
    let run = new Function(
        "proxyOfContext",
        `
            with(proxyOfContext){
                with(global){
                        ${code}
                }
            }
        `
    );
    return run(proxyOfContext);
};

This proxy object traps search of all variables and says: "yes, I have that variable." and If the proxy object doesn't really have that variable, show its value as undefined.

In this way, if any variable is defined in the macro code with the var statement, I can find it in the context object(like the vm module). But variables that are defined with let or const only available in that time and will not be saved in the context object(the vm module saves them but doesn't expose them).

Performance: Performance of this method is better than vm.runInContext.

safety: If you want to run code in a sandbox, this is not safe in any way, and you must use the vm module. It only provides a new namespace.

shabaany
  • 11
  • 2
  • A related question of mine: [Is using `with` statement with Proxies a bad practice?](https://stackoverflow.com/q/59601933/8376184) – FZs Jan 13 '21 at 09:47
0

Here's a good use for with: adding new elements to an Object Literal, based on values stored in that Object. Here's an example that I just used today:

I had a set of possible tiles (with openings facing top, bottom, left, or right) that could be used, and I wanted a quick way of adding a list of tiles which would be always placed and locked at the start of the game. I didn't want to keep typing types.tbr for each type in the list, so I just used with.

Tile.types = (function(t,l,b,r) {
  function j(a) { return a.join(' '); }
  // all possible types
  var types = { 
    br:  j(  [b,r]),
    lbr: j([l,b,r]),
    lb:  j([l,b]  ),  
    tbr: j([t,b,r]),
    tbl: j([t,b,l]),
    tlr: j([t,l,r]),
    tr:  j([t,r]  ),  
    tl:  j([t,l]  ),  
    locked: []
  };  
  // store starting (base/locked) tiles in types.locked
  with( types ) { locked = [ 
    br,  lbr, lbr, lb, 
    tbr, tbr, lbr, tbl,
    tbr, tlr, tbl, tbl,
    tr,  tlr, tlr, tl
  ] } 
  return types;
})("top","left","bottom","right");
0

You can use with to avoid having to explicitly manage arity when using require.js:

var modules = requirejs.declare([{
    'App' : 'app/app'
}]);

require(modules.paths(), function() { with (modules.resolve(arguments)) {
    App.run();
}});

Implementation of requirejs.declare:

requirejs.declare = function(dependencyPairs) {
    var pair;
    var dependencyKeys = [];
    var dependencyValues = [];

    for (var i=0, n=dependencyPairs.length; i<n; i++) {
        pair = dependencyPairs[i];
        for (var key in dependencyPairs[i]) {
            dependencyKeys.push(key);
            dependencyValues.push(pair[key]);
            break;
        }
    };

    return {
        paths : function() {
            return dependencyValues;
        },
    
        resolve : function(args) {
            var modules = {};
            for (var i=0, n=args.length; i<n; i++) {
                modules[dependencyKeys[i]] = args[i];
            }
            return modules;
        }
    }   
}
Dexygen
  • 12,287
  • 13
  • 80
  • 147
0

As Andy E pointed out in the comments of Shog9's answer, this potentially-unexpected behavior occurs when using with with an object literal:

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with ({num: i}) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "[object Object]"
  }
}

Not that unexpected behavior wasn't already a hallmark of with.

If you really still want to use this technique, at least use an object with a null prototype.

function scope(o) {
  var ret = Object.create(null);
  if (typeof o !== 'object') return ret;
  Object.keys(o).forEach(function (key) {
    ret[key] = o[key];
  });
  return ret;
}

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with (scope({num: i})) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "a"
  }
}

But this will only work in ES5+. Also don't use with.

Jackson
  • 9,188
  • 6
  • 52
  • 77
0

I am working on a project that will allow users to upload code in order to modify the behavior of parts of the application. In this scenario, I have been using a with clause to keep their code from modifying anything outside of the scope that I want them to mess around with. The (simplified) portion of code I use to do this is:

// this code is only executed once
var localScope = {
    build: undefined,

    // this is where all of the values I want to hide go; the list is rather long
    window: undefined,
    console: undefined,
    ...
};
with(localScope) {
    build = function(userCode) {
        eval('var builtFunction = function(options) {' + userCode + '}');
        return builtFunction;
    }
}
var build = localScope.build;
delete localScope.build;

// this is how I use the build method
var userCode = 'return "Hello, World!";';
var userFunction = build(userCode);

This code ensures (somewhat) that the user-defined code neither has access to any globally-scoped objects such as window nor to any of my local variables through a closure.

Just as a word to the wise, I still have to perform static code checks on the user-submitted code to ensure they aren't using other sneaky manners to access global scope. For instance, the following user-defined code grabs direct access to window:

test = function() {
     return this.window
};
return test();
kevin.groat
  • 1,274
  • 12
  • 21
0

with is useful coupled with shorthand object notation when you need to transform object structures from flat to hierarchical. So if you have:

var a = {id: 123, name: 'abc', attr1: 'efg', attr2: 'zxvc', attr3: '4321'};

So instead of:

var b = {
    id: a.id,
    name: a.name
    metadata: {name: a.name, attr1: a.attr1}
    extrastuff: {attr2: a.attr2, attr3: a.attr3}
}

You can simply write:

with (a) {
    var b = {
        id,
        name,
        metadata: {name, attr1}
        extrastuff: {attr2, attr3}
    }
}
nzn
  • 1,014
  • 11
  • 20
-1

Just wanted to add you can get "with()" functionality with pretty syntax and no ambiguity with your own clever method...

     //utility function
  function _with(context){
           var ctx=context;
           this.set=function(obj){
             for(x in obj){
                //should add hasOwnProperty(x) here
                ctx[x]=obj[x];
             }
       } 

       return this.set;          
 }

 //how calling it would look in code...

  _with(Hemisphere.Continent.Nation.Language.Dialect.Alphabet)({

      a:"letter a",
      b:"letter b",
      c:"letter c",
      d:"letter a",
      e:"letter b",
      f:"letter c",
     // continue through whole alphabet...

  });//look how readable I am!!!!

..or if you really want to use "with()" without ambiguity and no custom method, wrap it in an anonymous function and use .call

//imagine a deeply nested object 
//Hemisphere.Continent.Nation.Language.Dialect.Alphabet
(function(){
     with(Hemisphere.Continent.Nation.Language.Dialect.Alphabet){ 
         this.a="letter a";
         this.b="letter b";
         this.c="letter c";
         this.d="letter a";
         this.e="letter b";
         this.f="letter c";
         // continue through whole alphabet...
     }
}).call(Hemisphere.Continent.Nation.Language.Dialect.Alphabet)

However as others have pointed out, its somewhat pointless since you can do...

 //imagine a deeply nested object Hemisphere.Continent.Nation.Language.Dialect.Alphabet
     var ltr=Hemisphere.Continent.Nation.Language.Dialect.Alphabet 
     ltr.a="letter a";
     ltr.b="letter b";
     ltr.c="letter c";
     ltr.d="letter a";
     ltr.e="letter b";
     ltr.f="letter c";
     // continue through whole alphabet...
user2782001
  • 3,380
  • 3
  • 22
  • 41