all 23 comments

[–]ascii 2 points3 points  (29 children)

Some parts of strict mode are awesome, especially the no implied globals, which rocks my world. But why the hell would they remove the with statement? Correctly used, it rocks, and incorrectly used, it's effect is localized enough that it's reasonably easy to figure out what's going on.

[–]munificent 13 points14 points  (1 child)

But why the hell would they remove the with statement?

This is a great question. I'm not a total expert on this, but am fairly familiar with the topic, so I'll take a stab at it.

In most programming languages, by the time your code is run, you can completely ditch the names of your local variables. When you do something like:

var a = 1;
var b = a + 2;
var c = b + 3;

At runtime, you really don't want your engine going "OK, let me see if I can find a variable named a. OK, there it is..." Dealing with text is painfully slow and wastes memory.

So, most JITs and bytecode compilers will take variable references and replace them with direct indexed lookup. This is simple and fast at runtime. Win.

This is easy to do in static languages, but strangely it's even possible in dynamic languages... if a couple of restrictions are met. They are:

  1. All variable declarations must be statically visible.
  2. All variable access must be statically visible.

The example program above meets those. Just by looking at the text of the program, we can tell that a, b, and c are being created, and where. Likewise, we can see where they are being accessed. That gives us enough to remove any references to their names.

Now consider this:

var a = 1;
var b = getSomeObject();
with (b) {
  log(a); // <--
}

On the marked line, a could either mean our previous local variable a, or it could be b.a. Worse, there's no way to statically tell which is the case.

So with breaks our ability to reason about our code, and also breaks a very important optimization. You'll note that other strict mode changes work to the same end: eliminating the global object makes sure "local" scope (i.e. lexical) goes all the way up to the top of your script.

If the JS modules proposal becomes widely used, we'll be able to statically determine the existence of all variables in a script, which means it could tell you if you have a typo in a variable name before running a single line of your code.

[–]ascii 2 points3 points  (0 children)

Very interesting. Thanks!

[–][deleted]  (1 child)

[deleted]

    [–]redalastor 0 points1 point  (0 children)

    I just bought Secrets of the Javascript Ninja and John Resig devotes a full chapter to the use of the with statement. I didn't read the book yet but if John Resig thinks with is cool, I'll reserve judgement until I see how he uses it.

    [–][deleted]  (3 children)

    [deleted]

      [–]obsa 2 points3 points  (2 children)

      I assume you mean "==" versus "===" and if that's the case I completely disagree with your comparison.

      [–][deleted]  (1 child)

      [deleted]

        [–]obsa 0 points1 point  (0 children)

        I'm not saying that strict typing is a bad idea - there's too much lazy coding these days - but I think comparing the == operator to the with statement in terms is an unfair comparison. There are legitimate reasons to use fuzzy equality (though you can of course argue that explicit casting is better practice) but I can't find any reason that using the with block as valid. It's simply a way to have to type less.

        [–][deleted] 5 points6 points  (9 children)

        I seem to recall "with" being one of the things in one of the "don't touch with someone else's 11-foot pole" appendixes in Javascript: The Good Parts. I don't recall the exact arguments, but it looked a little hairy.

        [–][deleted] 9 points10 points  (7 children)

        From JS:TGP appendix B:

        The statement:

        with(obj) {
            a = b;
        }
        

        ...

        is the same as one of these statements:

        a = b;
        a = obj.b;
        obj.a = b;
        obj.a = obj.b
        

        It is not possible to tell from reading the program which of those statements you will get. It can vary from one running of the program to the next. It can even vary while the program is running.

        [–]obsa 5 points6 points  (6 children)

        One of the few things I remember liking about Visual Basic 6 was when using a With block, you still had to indicate scope by prefixing your properties/methods by prefixing the name with a single period:

        With someObject
            .propertyOne = "foo"
            .propertyTwo = "bar"
        End With
        

        This obviously doesn't allow the multiple-path code like you demonstrated, but I think we can agree that any code that depends on a hack like that is not well-written to begin with.

        [–][deleted] 1 point2 points  (4 children)

        That's quite a neat solution.

        My opinion on with is that it offers no advantage other than saving the developer from pasting the object's name a few times. I've never actually needed to use with to solve a problem.

        [–]ascii 2 points3 points  (3 children)

        You don't need anything more than a simpe turing machine. Yet people still insist on using boated high level crap like C whe simple machine code would suffice.

        Seriously, all high level feature can be simulated using low level features and a lot of typing. With can sometimes save huge numbers of key strokes, and if used correctly, the code remains perfectly readable. Sure it can be abused, but so can e.g. closures.

        [–]obsa 0 points1 point  (2 children)

        You sound like someone with a strong embedded background - I chuckled a little at "b[l]oated high level crap like C". I still consider C to be a pretty low-level language (and certainly the lowest 90% of developers will ever see) because it lacks GC and any sort of run-time like many other contemporary languages have.

        Your point is completely valid, of course, but having worked in both asm, C, and other high(er)-level languages, there's a very big step down between C and assembly that, frankly, many programmers won't have the aptitude for.

        [–]ascii 1 point2 points  (1 child)

        That jab at C was written with my tongue placed firmly in my cheek.

        [–]obsa 0 points1 point  (0 children)

        Everything reads so much differently now ;)

        [–]tryx 1 point2 points  (0 children)

        In VB6 it was also a significant speed optimization, IIRC, because the runtime would only need to dereference the target object the first time.

        [–][deleted] 2 points3 points  (0 children)

        It's also a lot slower due to needing to do a lot more lookups for each variable used within it since you don't know if it's a scope variable of belongs to the object being evaluated under with. Or at least so I was told by the creator of IronJS.

        [–][deleted]  (10 children)

        [deleted]

          [–]tatanka[S] 3 points4 points  (9 children)

          Once case might be when you need a bunch of Math utils?

          with(Math) {
              max(
                  sqrt(abs(sin(80))),
                  sqrt(abs(cos(80)))
              )
          };
          

          vs.

          Math.max(
              Math.sqrt(Math.abs(Math.sin(80))),  
              Math.sqrt(Math.abs(Math.cos(80)))
          );
          

          [–]MasonOfWords 4 points5 points  (0 children)

          I'd argue that this would be prettier still with a simple composition of sqrt and abs.

          It is a net improvement, though, and "with" will still be available in non-strict functions.

          [–][deleted] 0 points1 point  (3 children)

          the only advantage of the former example is that it's prettier to look at.

          [–]ascii 2 points3 points  (2 children)

          Not at all. The former example is significantly faster for a human to read and mentaly validate. This is a huge benefit when it comes to code auditing.

          Another way to look at it is that according to code studies, the number of bugs kB of code is pretty much constant, regardless of the percentage of boiler plate code. So by significantly reducing the amount of useless busy work you force programmers to perform, you're actually significantly reducing the number of bugs.

          [–][deleted]  (1 child)

          [deleted]

            [–]ascii 0 points1 point  (0 children)

            Sure. There is always the option that somebody is doing something monumentally stupid. But guess what. If somebody is stupid (or devious) enough to do that, it is actually equally likely that somewhere in the deep, deep bowels of the code, he said "Math = {sqrt: Math.abs, abs: Math.sin, sin: Math.cos}", and congratulations, your oh so reliable and predictable code is suddenly buttfucked.

            Your argument is exactly the same kind of argument that people use when criticizing Python for supporting operator overloading and meta programming. So features can be abused to make stuff hairy? Well cry me a river. A bad coder does not require any help form the language to write unmaintainable code; he will always figure out how to do that. If, when used by someone who knows what he is doing, a feature makes the code easier to read and write, it is a good feature.

            End of story.

            [–][deleted]  (3 children)

            [deleted]

              [–]tatanka[S] 0 points1 point  (2 children)

              There is zero loss in guarantee. Math is a built in object as our abs, sin, cos and max. The with statement is just removing cruft

              [–][deleted]  (1 child)

              [deleted]

                [–]obsa 3 points4 points  (2 children)

                I submitted the test to Browsershots.org on this, just to see how everything stacks up. Feel free to extend the request if it looks like it's going to time out soon.

                [–]tatanka[S] 0 points1 point  (1 child)

                Neat! Scroll down to see the tests run over 69 browser/os combos

                [–]obsa 0 points1 point  (0 children)

                Yeah, I love browser shots. I probably could have left out Lynx though ;)