3

编辑:这个问题的上下文是,参数是正在加载的模块的源字符串,它可能很大,并且有很多模块以这种方式加载,每个模块都有一个带有模块原始源代码的闭包当不需要或不需要时,使用内存将其保存在其中。

具体来说,我正在尝试修复此https://github.com/dojo/dojo/blob/master/dojo.js#L367代码,该代码将模块源泄漏到闭包中。

<script>
function closureTest(n) {
    function eval_(__text) {
        return eval(__text);
    }
    for (var i = 0; i < n; i++) {
        var m = eval_("(function(){ var x = 10; return function(n) { return x+n; }; })(window);");
        m(5);
    }
}
</script>
<button onclick="closureTest(1000)">Run</button>

在上面的代码中,如果在匿名函数中放置了一个断点,并且检查了存在的闭包,我们可以看到其中一个闭包包含 __text 和 arguments[0],其中包含模块的原始源代码传递给 eval_

以下是上述内容的变体:

<script>
function closureTest(n) {
    function eval_() {
        return eval(arguments[0]);
    }
    for (var i = 0; i < n; i++) {
        var m = eval_("(function(){ var x = 10; return function(n) { return x+n; }; })(window);");
        m(5);
    }
}
</script>
<button onclick="closureTest(1000)">Run</button>

在这种情况下,闭包不再包含 __text,但仍然包含 arguments[0] 以及传递给 eval_ 的字符串。

我能想到的最好的方法是以下内容,它在处理后删除 eval_ 的参数,副作用是现在正在定义的模块也作为名为 module 的变量出现在闭包中。

<script>
function closureTest(n) {
    function eval_() {
        var module = eval(arguments[0]);
        delete arguments[0];
        return module;
    }
    for (var i = 0; i < n; i++) {
        var m = eval_("(function(){ var x = 10; return function(n) { return x+n; }; })(window);");
        m(5);
    }
}
</script>
<button onclick="closureTest(1000)">Run</button>

有没有更好的方法来防止闭包保留传递给 eval_ 的参数副本?

4

1 回答 1

0

[编辑] 完全错过了问题的重点。

简短的回答:你不能。如您所知,闭包维护创建它的环境状态。没有办法“突破”闭包范围层次结构。

当您调用时,m()您不是在全局上下文中调用它,而是环境链的一部分,不仅是eval_闭包closureTest,还有——您可以看到返回的 eval'ed 函数也可以访问定义的invar在closureTest.

因此,您不能限制或突破封闭。但是,如果您在新作用域中定义了一个与先前闭包中的变量同名的新变量,您将无法直接访问该变量。因此,您的中间示例尽可能接近:

function closureTest(n) {
    function eval_() {
        return eval(arguments[0]);
    }
    for (var i = 0; i < n; i++) {
        var m = eval_("(function(){ var x = 10; return function(n) { return x+n; }; })(window);");
        m(5);
    }
 }

在这里,您返回的 eval'ed 函数无法访问eval_' 的参数,因为它自己arguments已经覆盖了它(但是,它仍然可以访问 in退出 for 循环)。这意味着,从功能上讲,经过评估的字符串无法访问传递给eval_.

笔记:

  • 是的,当您在 DevTools 中设置断点时,您可以检查父闭包,但这不会影响执行函数无法直接访问eval_`arguments[0]的事实。
  • 此外,该函数可以使用 etc 获取上层函数的字符串arguments.callee.caller.toString();。这仍然不允许函数直接访问重新定义的变量,但认为值得一提。
于 2013-11-12T17:11:31.500 回答