在JavaScript中安全使用“with”

Safe use of “with” in JavaScript

本问题已经有最佳答案,请猛点这里访问。

几乎所有我能找到的记录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扩展名的警告:防止愚蠢的程序员?


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使用链接的原因之一。它们在函数上使用callapply来控制this表示的内容,这意味着您调用this.something而不是with(this){something}

所以回答你的问题:在Javascript中有一个很好的使用with - 允许在90年代编写的IE4和IE5网站今天仍在使用。

而已。您不应该在新代码中使用它。


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只是将变量放在搜索变量的"地图"堆栈的顶部。

正常堆栈(从上到下搜索)

  • 窗口
  • (root - 只有window)

所以,如果你有

1
2
3
4
5
6
var foo = { document:"doc.pdf" };
window.myFunc = function(){
    with( foo ){
        alert( document );
    }
}

with内的堆栈是

  • FOO
  • 窗口
  • (root - 只有window)

它肯定会打印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(); //
}