NRaf
NRaf

Reputation: 7549

Clearing custom variables in the window object in Javascript

This is a tricky problem which has had me scratching my head for a few days.

I'm working on a project which involves taking an ten year old web application and reworking it as a single-page application. The application is huge - the time we have to work, quite tight, so some shortcuts had to be made.

Overall, however, I'm quite impressed with how far we've come, as we've had to overcome some interesting technical hurdles.

One involved clearing all custom window variables. Since we're dynamically reloading different pages of the application, we need to clear all custom variables so we don't get conflicts. What we do is firstly, load the base bootstrap of the application and save all the properties on the window object in an array.

Then, before we load each new page, we loop through the window properties and clear all the objects that aren't in our saved array (taking the window state back to before the page was loaded).

Now, this works fine in all the browsers we've tested except for IE7 and IE8 (which both need to be supported). The issue seems to be that global variables don't always seem to register on the window object.

Does anyone have any insight onto this problem? Any idea how to tackle for this IE7?

Any info will be appreciated.

EDIT: On bootstrap load we do this:

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

And then when we load a new page (via AJAX), we do:

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

FINAL SOLUTION:

In the end the easiest fix given the limited time was to simply change all variables defined as var x; to var x = null;

There was another solution I found, however. There was a small library here which I used as a starting point for an alternate solution: http://www.thomasfrank.se/global_namespace.html

It's not perfect (may need some tweaking to make it a little more stable, like adding a try-catch block around the AJAX call for example so that cross-domain scripts don't crash it). The way it works is that all external scripts files and internal scripts are parsed through, extracting a ton of words which can then be used to clear properties from the window object.

We actually experienced something very strange - this script wasn't properly picking up a lot of our variables... It turns out that it uses document.scripts to get all the loaded scripts on the page to be able to loop through them and parse them. The issue is that jQuery doesn't load external pages on the page in that way. What it does is simply pass the code to exec from what I know. As such, no script tag is actually added to the page.

The fix for this is to parse the raw AJAX response and store a reference to all the script tags (as well as, I suppose, extract the inline scripts) and then modify the library to be able to handle these files. This should work but all this processing going on was too scary for speed reasons - finding that we could simply do a search & replace on all variable definitions and have most of the job without the requirement of copious amounts of work for each page load made it obvious which path we should take.

Upvotes: 4

Views: 9581

Answers (6)

Max S.
Max S.

Reputation: 1461

    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);

First create a snapshot of the initial window variables (you don't need the entire window object), and store it in window.window_cache.

Then each time you switch the page in your app, run windowGarbageCollection(), which will compare window.window_cache to the current window and turn anything added afterward to false.

You'd want to run the garbage collection at beginning before the new page is rendered, so that when it's rendered any new variables won't get removed.

Upvotes: 0

James Westgate
James Westgate

Reputation: 11454

I would suggest using delete, a little known js feature to properly get rid of the global variables eg

delete window.varname;

OR

delete window["varname"];

Setting a variable to undefined is the equivalent of (in the global scope)

var varname;

which may not be what you want.

Upvotes: 3

nickaknudson
nickaknudson

Reputation: 4807

What if you wrapped the old script in a closure and saved/exported the variables that you need to persist?

Upvotes: 0

LetterEh
LetterEh

Reputation: 26706

As far as I can tell, if you want this to be a solution to "quickly" ensure that 100% of all global-variables are reset, and this is compatible with oldIE, you'll basically have to save the initial values of window/document properties which you modify... ...AND hand-write null values for each global on a particular page.

If you want it to be 100% accurate, fully BC with old-IE... AND without refactoring of any kind... ...that's what I forsee in your future.

Your company sounds like a few I know (I'm sure everyone knows a few) --
"Miracles can happen in no time for no money, if we tell the devs to make them happen."

If it came down to it, I would have picked through and fixed the global var issue by hand (or wrote a parser to find globally-scoped variables on a page -- or at least point to where they might be). The rest of the framework could be hacked together... ...though, I'd still end up wanting to sandbox pages, if at all possible (would make the whole state thing moot, for starters, once there were no globals).

But yeah... ...either null each page's globals by hand, or fix them, by hand, to apply them either as window[key] (if absolutely necessary), or as formerly_global_properties[key] or in an entirely different scope, enclosed in a function. All will be backwards-compatible, and all will be immeasurably awful.

But hacking the workaround like this and hacking the globals into something workable which can then be worked with/maintained at a later date are both going to be roughly the same amount of work, right?

Upvotes: 3

gaspyr
gaspyr

Reputation: 353

Have you checked if those properties that aren't showing up during looping are configured as non-enumerable. Because if an object's property isn't enumerable then it won't "show up" during a loop. you can check that with following method "propertyIsEnumerable".

example :

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

As a side note :

According to ECMAScript 5 The for/in loop runs the body of the loop once for each enumerable property (own or inherited) of the specified object, assigning the name of the property to the loop variable. Built-in methods that objects inherit are not enumerable, but the properties that your code adds to objects are enumerable (unless you make them nonenumerable).

Upvotes: 1

lbstr
lbstr

Reputation: 2832

Check out this question: JavaScript: List global variables in IE

In IE, it global variables aren't enumerable unless you explicitly define them as properties of the window object.

So, if you are assigning variables like this:

var number = 42; // not inside any function

It won't show up when you are iterating through window. You would have to define all of your globals like this:

window.number = 42; 

Or like this:

this.number = 42; 

Upvotes: 4

Related Questions