Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。
你觉得with语句在哪里有用?
Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。
你觉得with语句在哪里有用?
当前回答
使用with还会使代码在许多实现中变慢,因为现在所有内容都被包装在一个额外的查找范围中。在JavaScript中使用with没有合理的理由。
其他回答
你可以定义一个小的帮助函数来提供with的好处,而不会产生歧义:
var with_ = function (obj, func) { func (obj); };
with_ (object_name_here, function (_)
{
_.a = "foo";
_.b = "bar";
});
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编写的命令行控制台,我才真正意识到这个技术。我试图模拟Firebug/WebKit控制台api,其中特殊命令可以输入到控制台,但它们不会覆盖全局作用域中的任何变量。当我试图解决我在Shog9的精彩答案的评论中提到的一个问题时,我想到了这个。
为了达到这个效果,我使用了两个with语句将一个作用域“分层”到全局作用域后面:
with (consoleCommands) {
with (window) {
eval(expression);
}
}
这种技术的优点在于,除了性能方面的缺点外,它不会遭受with语句通常带来的恐惧,因为无论如何我们都是在全局作用域中求值——没有伪作用域之外的变量被修改的危险。
让我惊讶的是,当我设法找到在其他地方使用的相同技术时,我受到了启发,发布了这个答案——Chromium源代码!
InjectedScript._evaluateOn = function(evalFunction, object, expression) {
InjectedScript._ensureCommandLineAPIInstalled();
// Surround the expression in with statements to inject our command line API so that
// the window object properties still take more precedent than our API functions.
expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }";
return evalFunction.call(object, expression);
}
编辑:刚刚检查了Firebug的源代码,它们将4条语句链接在一起,形成更多的层。疯了!
const evalScript = "with (__win__.__scope__.vars) { with (__win__.__scope__.api) { with (__win__.__scope__.userVars) { with (__win__) {" +
"try {" +
"__win__.__scope__.callback(eval(__win__.__scope__.expr));" +
"} catch (exc) {" +
"__win__.__scope__.callback(exc, true);" +
"}" +
"}}}}";
它很适合将在相对复杂的环境中运行的代码放入容器中:我使用它来为“窗口”创建本地绑定,从而运行用于web浏览器的代码。