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

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


当前回答

只是想添加你可以得到“with()”功能与漂亮的语法和没有歧义与你自己的聪明的方法…

     //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!!!!

..或者,如果你真的想使用"with()"而不带歧义和自定义方法,可以将它包装在匿名函数中并使用.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)

然而,正如其他人所指出的,这有点毫无意义,因为你可以做……

 //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...

其他回答

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

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

使用with还会使代码在许多实现中变慢,因为现在所有内容都被包装在一个额外的查找范围中。在JavaScript中使用with没有合理的理由。

对于一些简短的代码片段,我想在度模式中使用sin, cos等三角函数,而不是在辐射模式中。为此,我使用了一个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 };
};

然后我可以使用三角函数在度模式没有进一步的语言噪音在一个with块:

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

这意味着:我使用一个对象作为函数的集合,我在有限的代码区域中启用它以便直接访问。我发现这很有用。

正如我之前的评论所指出的,我不认为你可以安全地使用它,无论它在任何给定的情况下有多么诱人。由于这里没有直接涉及到这个问题,我将重复一遍。考虑以下代码

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

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

如果不仔细研究这些函数调用,就无法知道这段代码运行后程序的状态是什么。如果user.name已经设置,它现在将是Bob。如果没有设置,全局名称将被初始化或更改为Bob,用户对象将保持没有name属性。

错误发生。如果你使用with,你最终会这样做,增加你的程序失败的机会。更糟糕的是,您可能会遇到在with块中设置全局变量的工作代码,或者是故意的,或者是作者不知道这个构造的怪处。这很像在开关上遇到了故障,你不知道作者是否有意这样做,也没有办法知道“修复”代码是否会引入回归。

现代编程语言充满了特性。一些特性,在使用多年后,被发现是不好的,应该避免使用。Javascript的with就是其中之一。

当您需要将对象结构从平面转换为层次结构时,With与简写对象表示法结合使用非常有用。如果你有:

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

所以不要:

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

你可以简单地写:

with (a) {
    var b = {
        id,
        name,
        metadata: {name, attr1}
        extrastuff: {attr2, attr3}
    }
}