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

你觉得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}
    }
}

其他回答

Visual Basic。NET有一个类似的With语句。我使用它的一个比较常见的方法是快速设置一些属性。而不是:

someObject.Foo = ''
someObject.Bar = ''
someObject.Baz = ''

,我可以这样写:

With someObject
    .Foo = ''
    .Bar = ''
    .Baz = ''
End With

这不仅仅是懒惰的问题。它还使代码可读性更强。与JavaScript不同的是,它不会出现歧义,因为您必须在受语句影响的所有内容前加上a。(点)。所以,下面两个是明显不同的:

With someObject
    .Foo = ''
End With

vs.

With someObject
    Foo = ''
End With

前者是someObject.Foo;后者是someObject作用域外的Foo。

我发现JavaScript缺乏区分使得它远不如Visual Basic的变体有用,因为歧义的风险太高了。除此之外,with仍然是一个强大的想法,可以提高可读性。

你可以使用with来避免在使用require.js时显式地管理arity:

var modules = requirejs.declare([{
    'App' : 'app/app'
}]);

require(modules.paths(), function() { with (modules.resolve(arguments)) {
    App.run();
}});

requirews .declare的实现:

requirejs.declare = function(dependencyPairs) {
    var pair;
    var dependencyKeys = [];
    var dependencyValues = [];

    for (var i=0, n=dependencyPairs.length; i<n; i++) {
        pair = dependencyPairs[i];
        for (var key in dependencyPairs[i]) {
            dependencyKeys.push(key);
            dependencyValues.push(pair[key]);
            break;
        }
    };

    return {
        paths : function() {
            return dependencyValues;
        },
    
        resolve : function(args) {
            var modules = {};
            for (var i=0, n=args.length; i<n; i++) {
                modules[dependencyKeys[i]] = args[i];
            }
            return modules;
        }
    }   
}

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

使用“with”可以使代码更加枯燥。

考虑下面的代码:

var photo = document.getElementById('photo');
photo.style.position = 'absolute';
photo.style.left = '10px';
photo.style.top = '10px';

你可以把它晾干到以下程度:

with(document.getElementById('photo').style) {
  position = 'absolute';
  left = '10px';
  top = '10px';
}

我想这取决于你是更喜欢易读性还是表达性。

第一个示例更容易读懂,可能推荐用于大多数代码。但是大多数代码都很平淡。第二种方法稍微晦涩一些,但它利用语言的表达特性来减少代码大小和多余的变量。

我想喜欢Java或c#的人会选择第一种方式(object.member),而喜欢Ruby或Python的人会选择后者。

我认为with的有用性取决于你的代码写得有多好。例如,如果你写的代码是这样的:

var sHeader = object.data.header.toString();
var sContent = object.data.content.toString();
var sFooter = object.data.footer.toString();

然后你可以这样说with可以提高代码的可读性:

var sHeader = null, sContent = null, sFooter = null;
with(object.data) {
    sHeader = header.toString();
    sContent = content.toString();
    sFooter = content.toString();
}

相反,你可能会认为你违反了得墨忒耳定律,但是,话说回来,也许没有。我跑题了。

最重要的是,要知道Douglas Crockford建议不要使用with。我敦促你看看他的博客文章关于与它的替代品在这里。