Are there legitimate uses for JavaScript's “with” statement?

后端 未结 30 1765
伪装坚强ぢ
伪装坚强ぢ 2020-11-22 04:22

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 ne

相关标签:
30条回答
  • 2020-11-22 05:03

    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.

    0 讨论(0)
  • 2020-11-22 05:03

    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.

    0 讨论(0)
  • 2020-11-22 05:03

    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.

    0 讨论(0)
  • 2020-11-22 05:05

    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 讨论(0)
  • 2020-11-22 05:06

    Hardly seems worth it since you can do the following:

    var o = incrediblyLongObjectNameThatNoOneWouldUse;
    o.name = "Bob";
    o.age = "50";
    
    0 讨论(0)
  • 2020-11-22 05:12

    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.

    0 讨论(0)
提交回复
热议问题