几乎所有我能找到的记录with的资源都警告不要使用它,主要是因为如果没有定义变量,它可能会产生不可预测的影响。
我想理解它,以便我可以有效地使用它 - 毕竟,它是有原因的。即使eval也有其非恶意用途!
因此,考虑到这一点,假设我想从元素中删除所有子节点,而不使用elem.innerHTML ="";
以下是安全的吗?
1
| with(elem) while(firstChild) removeChild(firstChild); |
请注意,此时我并不关心可读性,只关注功能。由于firstChild是所有元素节点的属性,并且removeChild是一个方法,因此以这种方式使用with应该没问题吧?
同样,假设我想设置一些样式。
1 2 3 4 5
| with(elem.style) {
color ="red";
backgroundColor ="black";
fontWeight ="bold";
} |
由于所有这些都是样式对象的属性(即使没有在样式表中定义,它们也是空字符串),可以这样使用with,对吗?
我是否遗漏了某些内容,或者是不断使用with类似于PHP的mysql扩展名的警告:防止愚蠢的程序员?
-
我投票保护免受滥用
-
请记住,with语句的使用会在严格模式下引发错误。所以我想说可能还有更多的东西,而不仅仅是"保护"(但现在不能说什么,而且规范在这一点上并不具体)。
-
我认为with的主要危险是变量暴露在整个范围内,但我看到了IIFE的变通方法。显然,当你可以在一个变量中缓存查找并使用它时,它非常慢?但是,我认为这种危险可能是一种优势。
-
eval有它的非邪恶用途吗?我可以想到一个,它不再适用:评估JSON字符串,但由于JSON.parse,我们没有必要。
-
请在询问前搜索:完全重复是否有JavaScript的"with"语句的合法用途?还有关:Javascript中with语句的未来
-
反对使用with的主要论点是相对难以推理它。
-
@Bergi我读的越多,我就越肯定已经提出了所有好的问题。
Javascript中的with关键字在90年代后期与VBScript竞争时有点令人反感。它仍然存在,但是如果您'use strict';则禁用,并且几乎每个Javascript验证器都会将其视为错误。
它有两个主要问题,都与Javascript中的作用域方式有关:
1 2 3 4 5 6 7
| var color ="green";
var foo ="bar";
with(elem.style) {
color ="red";
foo ="something else";
} |
现在color是什么?什么是foo?这不仅是令人困惑的代码,而且由于Javascript如何查找with范围内的变量,它也是非常慢的代码,因为现在每个语句都有一个额外的范围来搜索所使用的每个变量。
这就是为什么现代框架如jQuery和Prototype使用链接的原因之一。它们在函数上使用call和apply来控制this表示的内容,这意味着您调用this.something而不是with(this){something}。
所以回答你的问题:在Javascript中有一个很好的使用with - 允许在90年代编写的IE4和IE5网站今天仍在使用。
而已。您不应该在新代码中使用它。
with的唯一安全用途是根本不使用它。没有它就没有任务无法实现,现代标准在严格模式下完全禁用它。
出于所有目的,它被认为是设计错误,仅为了向后兼容而保留。
-
如果没有with和类似的简单性,你会如何编写这样的模板引擎?
-
@Bergi - Underscore模板引擎不需要with。搜索settings.variable的代码和文档。如果您不提供settings.variable,它仅使用with。
-
是的我知道,但是对于变量,模板源需要反复使用点访问器。如果避免使用shell,可以使用with(并且在那里很好)。
with语句只是写入对象的重复访问的简写:
对于
1 2
| foobar.foo.bar.baz = 'bubu';
foobar.foo.bar.buz = 'baba'; |
你可以写
1 2 3 4
| with(foobar.foo.bar){
baz = 'bubu';
buz = 'baba';
} |
真好!但是要修改foobar.foo.bar还是修改全局变量baz和buz?
在某些情况下,无法
JavaScript提供了更好的选择
1 2 3
| var better = foobar.foo.bar;
better.baz = 'bubu';
better.buz = 'baba'; |
现在没有歧义。
with只是将变量放在搜索变量的"地图"堆栈的顶部。
正常堆栈(从上到下搜索)
所以,如果你有
1 2 3 4 5 6
| var foo = { document:"doc.pdf" };
window.myFunc = function(){
with( foo ){
alert( document );
}
} |
with内的堆栈是
它肯定会打印foo.document而不是window.document。
在这种情况下,很明显你不应该像这样使用document,因为它通常在浏览器中使用。但是ECMAScript规范没有定义,所以在其他环境中,默认情况下其他变量可能在堆栈中(甚至更多)。
危险在于,无论你在with语句中调用的是什么,也都在堆栈上。
这将在document.url调用时失败:
1 2 3 4 5 6 7 8 9 10
| // Some 3rd-party library
function redirect( url ){
document.url = url; // url is undefined in document
}
var bar = { document:"20x20" };
with( bar ){
redirect(); //
} |
-
这里描述了实际的危险:yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
-
root和window之间有什么区别?而你的"危险"例子是完全错误的;你试过吗?
-
那是同样的情况。这个例子来自David Flanagan的一本书。
-
如果window是您的root范围,请不要将它们放在不同的子弹点上以避免混淆:-)哦,把那本书扔掉。自己测试代码。
-
不,这不是根范围。根范围是具有window的范围。然后是window。参见例如Firefox的源代码。
-
不要忘记JavaScript不仅适用于浏览器。它也是例如在Thunderbird,Adobe产品等中,window不是那里的主要变量。根上下文包含其他对象。
-
那么,window只是根作用域中的一个变量,它指向根作用域本身,不是吗?
-
不是,请尝试window.window.window
-
确实如此。
-
哈!几年前这没用了:)你有我。也许他们改变了这一点,以便与那样做的IE兼容。