JavaScript的“with”语句有合法用途吗?

388

Alan Storm的评论是对我关于with语句回答的回应,这让我开始思考。我很少找到使用这个特定语言功能的原因,并且从未考虑过它可能会引起麻烦。现在,我想知道如何有效地使用with,同时避免其缺点。

你在哪里发现with语句有用?


56
我从未使用过它。如果我假装它不存在,那么没有它的生活会更容易。 - Nosredna
7
或许它曾经有很多合理的用途,但现在已经无关紧要了。ES5 Strict 移除了 with,所以这个东西已经不存在了。 - Thomas Aylott
29
需要翻译的内容:Worth noting here that ES5 Strict is still optional.这里需要注意的是,ES5严格模式仍然是可选的 - Shog9
6
在 ES5 严格模式中,不是删除 'with',而是修改标准使得当 'with' 内没有找到变量时,任何赋值都会绑定到参数对象上,这样做是否更好呢? - JussiR
3
@JussiR:很可能会这样。但是这样做的问题在于,它可能会破坏旧版浏览器中的某些功能。 - Sune Rasmussen
显示剩余2条评论
33个回答

0

我正在开发一个项目,允许用户上传代码以修改应用程序的部分行为。在这种情况下,我使用with子句来防止他们修改我不想让他们乱搞的范围之外的任何内容。我用以下简化了的代码片段来实现这个功能:

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

这段代码(在某种程度上)确保用户定义的代码既不能访问任何全局作用域对象,如window,也不能通过闭包访问我的任何本地变量。

顺便提醒一句,我仍然需要对用户提交的代码执行静态代码检查,以确保他们没有使用其他狡猾的方式来访问全局作用域。例如,以下用户定义的代码直接访问了window

test = function() {
     return this.window
};
return test();

相关:https://dev59.com/q3RB5IYBdhLWcg3wtZIV - Shog9

0
正如Andy E在Shog9的回答评论中指出的那样,当使用对象字面量与with一起使用时,可能会出现这种潜在的意外行为:
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]"
  }
}

并不是意料之外的行为不是with 的标志。

如果你真的还想使用这种技术,至少使用一个原型为空的对象。

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

但这只适用于ES5+。同时不要使用with


-1

只是想要补充一下,您可以使用漂亮的语法和没有歧义的自己聪明的方法来获得“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...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接