【问题标题】:Changing the behavior of JavaScript's with statement to prevent assignment from modifying the global object更改 JavaScript with 语句的行为以防止赋值修改全局对象
【发布时间】:2011-06-30 12:50:47
【问题描述】:

JavaScript 中的with 语句首先检查对象的请求属性是否存在,然后再决定是否应该设置给定对象的属性或全局对象的属性。

例子:

var x = {a:5};

with(x){
    a = 6; //x.a is now 6
    b = 7; //window.b is now 7, x.b still does not exist
}

我想更改with 的行为,以便当它检查我正在使用的对象上是否存在属性时,它会始终将其视为存在,从而防止块内的分配意外修改全局对象。

这可以通过重载检查对象属性是否存在的函数来实现吗?例如这样的:

Object.prototype.__hasOwnProperty__ = function(p){
    return true;
}

var x = {a:5};

with(x){
    a = 6; //x.a is now 6
    b = 7; //x.b should now be 7
}

我的代码在 node.js 和 V8 上运行,因此解决方案是否仅适用于此并不重要。

希望有人知道我是如何实现这一点的。

感谢您的帮助!

【问题讨论】:

  • 问题是:必须重载哪个函数才能使 with 语句中的代码定义 b ond 对象 x 而不是在窗口/全局对象上定义它?
  • 您为什么不准确描述您想要完成的任务? with 语句不符合您的要求。您已经从上一个问题中知道了这一点。是否有某些原因您不会寻找更适合您使用的语言的任何解决方案?
  • 我想做的事:我想将全局对象与在 with 语句中执行的代码完全隔离开来。代码应该根本无法访问全局对象。这对我来说很重要。想象一下,您开发了一个无需重启即可 24/7 运行的服务器。该服务器使用 eval() 命令执行非常不同的脚本。当脚本在全局对象上定义变量时会发生什么?它会一直存在,直到服务器重新启动。这是内存泄漏;)
  • 想象一下,你的 apache 服务器会将你的 php 曾经定义的所有全局变量保存在内存中,并且只会在重新启动时杀死它们;)你的内存当然不会开心;)
  • @FlashFan:您正在运行的脚本使用eval() 有什么原因吗?我不想这么说,但这是通常建议您避免使用的 javascript 的另一个功能。看来您正在尝试使用通常被认为是 javascript 的坏部分

标签: javascript while-loop


【解决方案1】:

我找到了一种完全隔离脚本的方法。这不是好的代码,但它可以工作。

X = 5;
function ExecuteIsolated(code){
    var vars = [];
    (function(){
        for(var v in window){
            vars.push(v);                   
        }           
        eval("var "+vars.join(",")+";");

        //Totally isolated code here:           
        eval(code);     
        //End of totally isolated code
    })();
    var i = 0;
    for(var v in window){
        if(vars[i++] != v){
            delete window[v];
            i--;
        }
    }
}       
ExecuteIsolated("X = 8");
console.log(X);

也许它对其他人有用;)

【讨论】:

  • 您知道for-in 不提供枚举的保证顺序吗?正在评估什么代码?代码是否来自已知且受信任的来源?
  • 是的,它是受信任的来源,但我不能保证代码不会在全局对象中存储太多数据的来源。
【解决方案2】:

要将变量排除在全局范围之外,您可以使用匿名函数;由于 JavaScript 具有函数作用域,因此函数中定义的所有变量在作用域内都是局部的。不过,它要求您正确声明变量,但我强烈建议您在任何情况下都这样做。

(function () {
    var a = 1;
    alert("a is... " + a);
    // do more stuff here
}());
alert(typeof a); // should be undefined

示例:http://jsfiddle.net/AWDzV/

这并没有专门处理您的with 声明,但它可能是完成您正在寻找的最佳方式。然后,要模拟with 语句的行为,您所要做的就是使用一个变量:

(function () {
    var a = 1,
        some_very_long_object_name = {a: 1, b: 2, c: 3},
        obj = some_very_long_object_name;
    obj.a = 2;
    obj.b = 3;
    obj.c = 4;
}());
alert(typeof a); // should be undefined

不是世界上最好的代码,但它确实证明了使用短变量名替换长对象名的效果很好。总而言之,我觉得这可能是最好的解决方案。

当然,任何未正确声明的变量都会“泄漏”到全局范围内:

(function () {
    a = 1; // notice the lack of "var" keyword
}());
alert(a); // should be 1

示例:http://jsfiddle.net/AWDzV/1/

但是您可以使用JSLint 来帮助捕获该特定错误。在上面的代码上运行它会产生以下错误消息:

错误:隐含全局:a 2,4,警报 4

【讨论】:

    【解决方案3】:

    您正试图从根本上改变 with 语句在 JavaScript 中的工作方式。这是不可能的,因为您无法保证解释器使用hasOwnProperty(或您有权访问的任何其他构造)来检查您正在使用的对象上是否存在属性。

    你加入了一个很好的传统,希望with 以不同的方式工作,但事实并非如此。它的工作原理是这样的,最好避免。

    【讨论】:

    • 您是否有其他想法,我如何才能达到我帖子的 cmets 中描述的行为?
    • @FlashPan - 这是不可能的。这只是 JS 工作方式的一部分。有一个隐含的全局对象。你必须忍受它。遵循最佳实践可以保护您免受许多问题的困扰。始终使用var 声明变量并避免使用with 语句。
    • ^^ 好的。 Node.js 可能真的很好,但由于这个事实,我怀疑它真的可以用于大型和高性能项目:S
    • 我现在编写了一个可以满足我要求的技巧。我将其添加为答案 ;)
    【解决方案4】:

    两件事:

    1. 不要修改Object.prototype。这被认为是不好的做法,并且到处都会导致意想不到的结果,而且如果它被修改,其他 JS 框架甚至都无法运行。

    2. 请不要使用with。它已从 javascript 中弃用,因为当存在与属性同名的局部变量时,无法确定它应该如何发挥最佳功能。

    如果您需要遍历对象的属性,只需这样做:

    for (var i in myObject) {
        if (myObject.hasOwnProperty(i)) {
            // processing logic here
        }
    }
    

    您提到了可以帮助您实现覆盖该设置逻辑的代码。 https://developer.mozilla.org/en/JavaScript/Guide/Working_with_Objects 提供了有关使用 defineSetter 覆盖设置部分的信息,但强烈建议使用它,即使在 JavaScript 1.8.1 及更高版本中也不可能。我会推荐另一种方法。

    【讨论】:

    • ^^ 但这将是我在以下帖子中想要做的唯一方法:stackoverflow.com/questions/5052599/…
    • 告诉我你想要完成什么。
    • @Eli 我很确定他正在尝试改变with 的行为,以便当它检查上面的x 是否已经具有属性b 时,它会像对待它一样对待它,因此x.b 被分配7 而不是全局对象。
    • @Eli 换句话说,他试图让with 的行为不存在你在#2 中提到的问题。
    • 无法更改with 的功能。就像我说的,推荐一个可以实现相同目的的辅助方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多