【问题标题】:Safe use of "with" in JavaScript [duplicate]在 JavaScript 中安全使用“with”[重复]
【发布时间】:2013-06-21 23:33:42
【问题描述】:

我能找到的几乎所有记录 with 的资源都警告不要使用它,主要是因为如果未定义变量,它可能会产生不可预知的影响。

我想了解它,以便有效地利用它——毕竟,它的存在是有原因的。即使eval 也有它的非邪恶用途!

因此,考虑到这一点,假设我想从一个元素中删除所有子节点,而不使用 elem.innerHTML = "";

以下是否安全?

with(elem) while(firstChild) removeChild(firstChild);

请注意,此时我关心的不是可读性,而是功能。由于firstChild 是所有元素节点的属性,removeChild 是所有元素节点的方法,所以这样使用with 应该没问题,对吧?

同样,假设我想设置一些样式。

with(elem.style) {
    color = "red";
    backgroundColor = "black";
    fontWeight = "bold";
}

由于所有这些都是样式对象的属性(即使没有在样式表中定义,它们也是空字符串),这样使用with 就可以了,对吧?

我是否遗漏了什么,或者是不断警告不要使用 with 类似于针对 PHP 的 mysql 扩展的警告:防止愚蠢的程序员?

【问题讨论】:

  • 我投票支持防止滥用
  • 请记住,使用with 语句会在严格模式下引发错误。所以我想说它可能不仅仅是“保护”(但现在不能说什么,规范在这一点上并不具体)。
  • 我认为with 的主要危险是变量暴露在整个范围内,但我已经看到了使用 IIFE 的解决方法。显然,当您可以将查找缓存在变量中并使用它时,它 非常 慢吗?但我认为这种危险可能是一种优势。
  • eval 有它的非邪恶用途吗?我能想到一个不再适用的方法:评估 JSON 字符串,但感谢 JSON.parse,我们不必这样做。

标签: javascript


【解决方案1】:

Javascript 中的 with 关键字有点倒退到 90 年代后期它与 VBScript 竞争的时候。它仍然存在,但如果你 'use strict'; 并且几乎所有 Javascript 验证器都认为它存在错误,它就会被禁用。

它有两个主要问题,都与 Javascript 中作用域的工作方式有关:

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 网站今天仍然有效。

就是这样。您不应该在新代码中使用它。

【讨论】:

  • 我喜欢你的想法。 +1。
【解决方案2】:

with 语句只是编写循环访问对象的简写:

对于

foobar.foo.bar.baz = 'bubu';
foobar.foo.bar.buz = 'baba';

你可以写

with(foobar.foo.bar){
    baz = 'bubu';
    buz = 'baba';
}

太好了!但是 foobar.foo.bar 会被修改还是全局变量 baz 和 buz 会被修改? 在某些情况下,无法确定。

JavaScript 提供了更好的选择

var better = foobar.foo.bar;
better.baz = 'bubu';
better.buz = 'baba';

现在没有歧义了。

【讨论】:

    【解决方案3】:

    with 的唯一安全使用是根本不使用它。没有它就没有任何任务无法完成,现代标准在严格模式下完全禁用它。

    出于所有目的,它被视为设计错误,仅为向后兼容而保留。

    【讨论】:

    • 如果没有with 和类似的简单性,您将如何编写模板引擎like this
    • @Bergi - 下划线模板引擎不需要with。在代码和文档中搜索settings.variable。如果您不提供settings.variable,它只会使用with
    • 是的,我知道,但是模板源需要反复使用点访问器。如果避免使用它们,您可以使用with(那里没问题)。
    【解决方案4】:

    with 只是将变量放在搜索变量的“映射”堆栈的顶部。

    普通堆栈是(从上到下搜索)

    • 窗口
    • (root - 只需要window)

    如果你有

    var foo = { document: "doc.pdf" };
    window.myFunc = function(){
        with( foo ){
            alert( document );
        }
    }
    

    with 内的堆栈是

    • 窗口
    • (root - 只拥有window

    它肯定会打印foo.document 而不是window.document

    在这种情况下,很明显您不应该像那样使用document,因为它通常用于浏览器。但是 ECMAScript 规范并没有定义,所以在其他环境中,其他变量可能默认在堆栈上(甚至更多)。

    危险在于,无论您在with 语句中调用的任何内容,堆栈中都存在该内容。 这将在document.url 调用中失败:

    // Some 3rd-party library
    function redirect( url ){
        document.url = url;    // url is undefined in document
    }
    
    var bar = { document: "20x20" };
    
    with( bar ){
        redirect(); //
    }
    

    【讨论】:

    • rootwindow 有什么区别?而你的“危险”例子是完全错误的;你试过了吗?
    • 同样的情况。这个例子来自大卫弗拉纳根的一本书。
    • 如果window 你的root 范围,不要将它们放在不同的项目符号上以避免混淆:-) 哦,把那本书扔掉。 自己测试代码
    • 不,它不是根范围。根范围是具有window 的范围。然后是window。参见例如火狐的源代码。
    猜你喜欢
    • 2019-07-22
    • 2014-03-17
    • 1970-01-01
    • 1970-01-01
    • 2015-09-03
    • 1970-01-01
    • 1970-01-01
    • 2015-08-19
    • 1970-01-01
    相关资源
    最近更新 更多