3

这是一个棘手的问题,让我摸不着头脑好几天了。

我正在从事一个项目,该项目涉及采用一个已有十年历史的 Web 应用程序并将其重新设计为单页应用程序。该应用程序非常庞大——我们必须工作的时间非常紧迫,因此必须做出一些捷径。

然而,总的来说,我对我们所取得的进展印象深刻,因为我们必须克服一些有趣的技术障碍。

一项涉及清除所有自定义窗口变量。由于我们正在动态地重新加载应用程序的不同页面,因此我们需要清除所有自定义变量,以免发生冲突。我们首先要做的是加载应用程序的基本引导程序并将窗口对象的所有属性保存在一个数组中。

然后,在我们加载每个新页面之前,我们遍历窗口属性并清除所有不在我们保存的数组中的对象(将窗口状态恢复到加载页面之前)。

现在,这在我们测试过的所有浏览器中都可以正常工作,除了 IE7 和 IE8(两者都需要支持)。问题似乎是全局变量似乎并不总是在窗口对象上注册。

有人对这个问题有任何见解吗?知道如何解决这个 IE7 问题吗?

任何信息将不胜感激。

编辑:在引导加载时,我们这样做:

for (i in window) {
   this.globalVars[i] = 1;
}

然后当我们加载一个新页面(通过 AJAX)时,我们会:

for (i in window) {
   if (!this.globalVars[i]){
       window[i] = undefined;
   }
}

最终解决方案:

最后,鉴于时间有限,最简单的解决方法是简单地更改定义为 var x 的所有变量;到 var x = null;

但是,我找到了另一种解决方案。这里有一个小型库,我用它作为替代解决方案的起点:http ://www.thomasfrank.se/global_namespace.html

它并不完美(可能需要进行一些调整以使其更加稳定,例如在 AJAX 调用周围添加一个 try-catch 块,以便跨域脚本不会使其崩溃)。它的工作方式是解析所有外部脚本文件和内部脚本,提取大量单词,然后可以使用这些单词从窗口对象中清除属性。

我们实际上经历了一些非常奇怪的事情——这个脚本没有正确地获取我们的很多变量......原来它使用 document.scripts 来获取页面上所有加载的脚本,以便能够遍历它们并解析他们。问题是 jQuery 不会以这种方式在页面上加载外部页面。它所做的只是将我所知道的代码传递给 exec 。因此,实际上没有将脚本标签添加到页面中。

对此的解决方法是解析原始 AJAX 响应并存储对所有脚本标记的引用(以及,我想,提取内联脚本),然后修改库以能够处理这些文件。这应该可以工作,但是由于速度原因,所有这些处理都太可怕了——发现我们可以简单地对所有变量定义进行搜索和替换,并且完成大部分工作,而不需要为每个页面加载大量工作。很明显我们应该走哪条路。

4

6 回答 6

4

看看这个问题:JavaScript:在 IE 中列出全局变量

在 IE 中,除非您将全局变量明确定义为窗口对象的属性,否则它是不可枚举的。

因此,如果您要分配这样的变量:

var number = 42; // not inside any function

迭代时它不会显示window。您必须像这样定义所有全局变量:

window.number = 42; 

或者像这样:

this.number = 42; 
于 2012-08-12T22:46:08.073 回答
3

据我所知,如果您希望这是“快速”确保 100% 重置所有全局变量的解决方案,并且这与 oldIE 兼容,您基本上必须保存 window 的初始值/document 您修改的属性... ...并在特定页面上为每个全局手写输入空值。

如果您希望它是 100% 准确的,完全 BC 与旧的 IE ......并且没有任何形式的重构......这就是我对你未来的预期。

你的公司听起来像我认识的几个(我相信每个人都知道一些)——
“如果我们告诉开发人员让它们发生,奇迹就会在不花钱的情况下立即发生。”

如果归根结底,我会选择并手动修复全局 var 问题(或者编写一个解析器来在页面上查找全局范围的变量——或者至少指向它们可能在的位置)。框架的其余部分可以被破解在一起......不过,如果可能的话,我仍然最终想要沙箱页面(对于初学者来说,一旦没有全局变量,整个状态就会变得毫无意义) .

但是是的... ...要么手动将每个页面的全局变量设为空,要么手动修复它们,以将它们应用为window[key](如果绝对必要的话),或者作为formerly_global_properties[key]或在一个完全不同的范围内,包含在一个函数中。一切都将向后兼容,一切都将变得无比糟糕。

但是像这样破解解决方法并将全局变量破解为可以在以后使用/维护的可行的东西,两者的工作量大致相同,对吧?

于 2012-08-16T00:56:42.333 回答
3

我建议使用删除,一个鲜为人知的 js 功能来正确摆脱全局变量,例如

delete window.varname;

或者

delete window["varname"];

将变量设置为 undefined 相当于(在全局范围内)

var varname;

这可能不是你想要的。

于 2012-08-16T11:47:08.360 回答
1

您是否检查过那些在循环期间未显示的属性是否配置为不可枚举。因为如果一个对象的属性不可枚举,那么它就不会在循环期间“显示”。您可以使用以下方法“propertyIsEnumerable”进行检查。

例子 :

var o = {x:1, y:2, z:3}; // Three enumerable own properties
o.propertyIsEnumerable("toString") // => false: not enumerable

作为旁注:

根据 ECMAScript 5 for/in 循环为指定对象的每个可枚举属性(自己的或继承的)运行一次循环体,将属性的名称分配给循环变量。对象继承的内置方法是不可枚举的,但您的代码添加到对象的属性是可枚举的(除非您将它们设为不可枚举)。

于 2012-08-12T22:46:42.153 回答
0

如果您将旧脚本包装在一个闭包中并保存/导出您需要保留的变量怎么办?

于 2012-08-16T01:03:39.583 回答
0
    clone(obj) {
        var _ = this,
            copy;
        if (null == obj || "object" != typeof obj) return obj;
        if (obj instanceof Date) {
            copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }
        if (obj instanceof Array) {
            copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                copy[i] = _.clone(obj[i]);
            }
            return copy;
        }
        if (obj instanceof Object) {
            copy = {};
            for (var attr in obj) {
                if (obj.hasOwnProperty(attr)) copy[attr] = _.clone(true);
            }
            return copy;
        }
        throw new Error("Unable to copy obj");
    }

    windowGarbageCollection(){
        var removeObjects = Object
            .keys(window)
            .map( (currentValue) => {
                if(!window.window_cache[currentValue]){
                    if(currentValue != 'window_cache' && currentValue != '0'){
                        window[currentValue] = false;
                    }
                }
            });
    }

    // run this at the moment you want to grab the initial variables
    // probably at the very beginning
    window.window_cache = clone(window);

首先创建初始window变量的快照(您不需要整个窗口对象),并将其存储在window.window_cache.

然后每次在应用程序中切换页面时,运行windowGarbageCollection(),它将window.window_cache与当前进行比较window并将之后添加的任何内容转为false.

您希望在呈现新页面之前开始运行垃圾收集,以便在呈现新页面时不会删除任何新变量。

于 2017-07-17T15:59:07.117 回答