Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protecting a Global Javascript "API" Object

I currently have a Web Application that runs off a global Javascript-based API, and it is initialized like this:

var Api = {
    someVar: "test",
    someFunction: function() {
        return "foo";
    }
}

This API is shared across many "Widgets" that live in the Web Application, and they should all run off this single Api instance so they can pass data to each other.

AJAX is currently used to load these Widgets, for example in widgets/mywidget.html, and it's placed in, say, <div id='widget_<random number>'>...</div>

Certain other parts of the code may choose to add more functionality to Api, and it's currently done like this:

Api.myExtension = {
    myNewFunction: function() {
        return "bar";
    }
}

However, some issues arise from this kind of usage:

Problem One: What if one Widget (these may be provided by third-parties) decides to hide some code within, and does something similar to Api = {}, destroying the global Api var everything lives on, and breaking the whole Application? Is it possible to protect this Api variable from being overwritten from outside? Only "extending" is allowed (adding new things), but "removing/changing" is not allowed. i.e.:

Api.foo = { test: "bar" } // allowed
Api.someVar = "changing the existing someVar"; // not allowed

The following code is located "inside" Api, for example:

var Api = {
    Debug: {
        Messages = new Array,
        Write: function() {
            Api.Debug.Messages.push("test"); // allowed
        }
    }
}

Api.Debug.Messages.push("test 2"); // not allowed

Probable Solutions I've Thought Of:

  1. Suppose we simply use frames to resolve this issue. The Apis provided are now separate from each other. However, there's additional overhead when loading Api again and again if I have many Widgets running, and they can no longer communicate with the "Host" of the widgets (the page where frames reside in), for example, I may want to tell the host to show a notification: Api.Notify.Show("Test"), but it cannot do so because this Api is completely independent from other instances, and it cannot communicate with the "Host"

  2. Using something like a "getter" and "setter" function for the Api to be read and written. I'm unsure on how to implement this, so any help on directions on how to implement this is welcome!

  3. A mixture of 1/2?

like image 578
Jimmie Lin Avatar asked Oct 13 '12 02:10

Jimmie Lin


2 Answers

There's no good way to prevent having a "third party" widget overwrite the a global variable. Generally it is the responsibility of whoever is putting together the final application to ensure that whatever JavaScripts they are using aren't littering the global namespace and conflicting. The best thing you can do in that direction is give your "Api" a nice, unique name.

What I think can help you a lot is something like the "revealing pattern", which would be a way of doing the "getters and setters" you mentioned, plus more if you needed it.

A simple, useless example would be like the following:

var Api = (function () {
    // private variable
    var myArray = [];

    return {
        addItem: function (newItem) {
            myArray.push(newItem);
        },
        printItems: function () {
            console.log("lots if items");
        }
    };
})();

Api.addItem("Hello, world");
Api.extensionValue = 5;

I think you should make a clear delineation of what is shared, or "singleton" data, and keep those items private, as with myArray in my example.

like image 141
BobS Avatar answered Sep 20 '22 06:09

BobS


Make it a constant:

const Api = "hi";

Api = 0;

alert(Api); //"hi"
like image 26
Kernel James Avatar answered Sep 20 '22 06:09

Kernel James