Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。

你觉得with语句在哪里有用?


当前回答

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
}

归结起来就是

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

你能相信这么低质量的代码吗?不,我们看到它是完全不可读的。这个例子无可否认地证明了没有必要使用with-statement,如果我的可读性是正确的;)

其他回答

下面是with的一个很好的用法:根据存储在Object中的值向Object Literal添加新元素。这是我今天用的一个例子:

我有一组可能使用的瓷砖(面向顶部、底部、左侧或右侧),我想快速添加一个瓷砖列表,在游戏开始时始终放置并锁定这些瓷砖。我不想一直输入类型。tbr用于列表中的每种类型,所以我只使用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");

我认为最明显的用途是作为捷径。例如,如果你初始化一个对象,你只需节省大量输入“ObjectName”。有点像lisp的“带槽”,让你可以写

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

这和写作是一样的吗

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

当你的语言允许“Objectname”时,这是一个更明显的快捷方式。但是还是。

我认为对象文字的使用很有趣,就像使用闭包的一个插入式替换

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]);
}

或者与闭包等价的with语句

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) };
       }        
}

我认为真正的风险是不小心操纵不属于with语句的变量,这就是为什么我喜欢将对象文字传递给with,你可以确切地看到它将在代码中添加的上下文中。

CoffeeScript的Coco fork有一个with关键字,但它只是简单地将this(在CoffeeScript/Coco中也可以写成@)设置为块中的目标对象。这消除了歧义,实现了ES5严格模式兼容:

with long.object.reference
  @a = 'foo'
  bar = @b

我创建了一个“merge”函数,它消除了with语句的一些歧义:

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;
    };
}

我可以类似地使用它,但我可以知道它不会影响任何我不想让它影响的范围。

用法:

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()});
}