Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。
你觉得with语句在哪里有用?
Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。
你觉得with语句在哪里有用?
当前回答
with语句可用于减少代码大小或用于私有类成员,例如:
// demo class framework
var Class= function(name, o) {
var c=function(){};
if( o.hasOwnProperty("constructor") ) {
c= o.constructor;
}
delete o["constructor"];
delete o["prototype"];
c.prototype= {};
for( var k in o ) c.prototype[k]= o[k];
c.scope= Class.scope;
c.scope.Class= c;
c.Name= name;
return c;
}
Class.newScope= function() {
Class.scope= {};
Class.scope.Scope= Class.scope;
return Class.scope;
}
// create a new class
with( Class.newScope() ) {
window.Foo= Class("Foo",{
test: function() {
alert( Class.Name );
}
});
}
(new Foo()).test();
如果你想要修改作用域,with语句是非常有用的,这对于拥有自己的全局作用域是必要的,可以在运行时操作。你可以在它上面放置常量或者某些常用的辅助函数,比如:“toUpper”,“toLower”或“isNumber”,“clipNumber”麻生..
关于糟糕的性能,我经常读到:作用域函数不会对性能产生任何影响,事实上,在我的FF中,有作用域的函数比无作用域的函数运行得更快:
var o={x: 5},r, fnRAW= function(a,b){ return a*b; }, fnScoped, s, e, i;
with( o ) {
fnScoped= function(a,b){ return a*b; };
}
s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
r+= fnRAW(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );
s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
r+= fnScoped(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );
所以在上面提到的方式中使用with语句对性能没有负面影响,但它减少了代码大小,这影响了移动设备上的内存使用。
其他回答
我认为在将模板语言转换为JavaScript时,with语句可以派上用场。例如base2中的JST,但我更经常看到它。
我同意不使用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还会使代码在许多实现中变慢,因为现在所有内容都被包装在一个额外的查找范围中。在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;
}
}
这意味着:我使用一个对象作为函数的集合,我在有限的代码区域中启用它以便直接访问。我发现这很有用。
对代理对象使用“with”语句
我最近想为babel写一个支持宏的插件。我想有一个单独的变量名称空间来保存我的宏变量,我可以在这个空间中运行我的宏代码。此外,我想检测宏代码中定义的新变量(因为它们是新的宏)。
首先,我选择了vm模块,但我发现vm模块中的全局变量,如Array, Object等与主程序不同,我无法实现模块并要求与该全局对象完全兼容(因为我无法重构核心模块)。最后,我找到了“with”语句。
const runInContext = function(code, context) {
context.global = context;
const proxyOfContext = new Proxy(context, { has: () => true });
let run = new Function(
"proxyOfContext",
`
with(proxyOfContext){
with(global){
${code}
}
}
`
);
return run(proxyOfContext);
};
这个代理对象捕获所有变量的搜索,并说:“yes, I have that variable.”如果代理对象实际上没有该变量,则将其值显示为undefined。
这样,如果在宏代码中使用var语句定义了任何变量,我可以在上下文对象中找到它(如vm模块)。但是用let或const定义的变量只在该时间内可用,不会保存在context对象中(vm模块保存它们,但不公开它们)。
性能:该方法性能优于vm.runInContext。
安全:如果你想在沙箱中运行代码,这在任何方面都不安全,你必须使用vm模块。它只提供一个新的名称空间。