5

我目前有一个运行全局基于 Javascript 的 API 的 Web 应用程序,它的初始化如下:

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

这个 API 在 Web 应用程序中的许多“小部件”之间共享,它们都应该在这个单一Api实例上运行,以便它们可以相互传递数据。

AJAX 当前用于加载这些小部件,例如在 widgets/mywidget.html 中,它被放置在,比如说,<div id='widget_<random number>'>...</div>

代码的某些其他部分可能会选择向 中添加更多功能Api,目前是这样完成的:

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

但是,这种用法会产生一些问题:

问题一:如果一个 Widget(这些可能由第三方提供)决定在其中隐藏一些代码,并执行类似的操作Api = {},破坏所有存在的全局Api变量,并破坏整个应用程序怎么办?是否可以保护此Api变量不被外部覆盖?只允许“扩展”(添加新事物),但不允许“删除/更改”。IE:

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

以下代码位于“内部” Api,例如:

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

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

我想到的可能的解决方案:

  1. 假设我们只是使用框架来解决这个问题。提供的Apis 现在彼此分开。但是,Api如果我有很多 s 正在运行,那么在一次又一次加载时会有额外的开销Widget,并且它们无法再与小部件的“主机”(框架所在的页面)通信,例如,我可能想告诉主机显示通知:Api.Notify.Show("Test"),但它不能这样做,因为这Api完全独立于其他实例,并且它无法与“主机”通信

  2. 使用诸如“getter”和“setter”之类的函数来读取和写入 Api。我不确定如何实现这一点,因此欢迎就如何实现这一点提供任何帮助!

  3. 1/2的混合物?

4

4 回答 4

2

没有好的方法可以防止“第三方”小部件覆盖全局变量。通常,组装最终应用程序的人有责任确保他们使用的任何 JavaScript 都不会乱扔全局命名空间并发生冲突。在这个方向上你能做的最好的事情就是给你的“Api”一个好听的、独特的名字。

我认为可以帮助您很多的是“揭示模式”之类的东西,这将是一种执行您提到的“getter and setter”的方式,如果您需要它,还可以添加更多。

一个简单、无用的示例如下所示:

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;

我认为您应该清楚地描述共享的数据或“单例”数据,并将这些项目保密,就像我的示例中的 myArray 一样。

于 2012-10-13T03:14:16.740 回答
2

看一眼

Object.freeze

更多信息在这里

这是来自 Mozilla 页面的代码示例:

var obj = {
  prop: function (){},
  foo: "bar"
};

// New properties may be added, existing properties may be changed or removed
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;

var o = Object.freeze(obj);

assert(Object.isFrozen(obj) === true);

// Now any changes will fail
obj.foo = "quux"; // silently does nothing
obj.quaxxor = "the friendly duck"; // silently doesn't add the property

// ...and in strict mode such attempts will throw TypeErrors
function fail(){
  "use strict";
  obj.foo = "sparky"; // throws a TypeError
  delete obj.quaxxor; // throws a TypeError
  obj.sparky = "arf"; // throws a TypeError
}

fail();

// Attempted changes through Object.defineProperty will also throw
Object.defineProperty(obj, "ohai", { value: 17 }); // throws a TypeError
Object.defineProperty(obj, "foo", { value: "eit" }); // throws a TypeError

但是浏览器支持仍然是部分的

编辑:请参阅内核詹姆斯的回答,它与您的问题更相关(冻结将保护对象,但不保护重新分配它。但是 const 会)同样的问题,但浏览器支持有限。

于 2012-10-13T03:20:35.013 回答
2

使它成为constant

const Api = "hi";

Api = 0;

alert(Api); //"hi"
于 2012-10-13T04:02:22.987 回答
1

保护全局变量的唯一方法(至少我能想到的)是防止小部件直接访问它。正如您所建议的,这可以通过使用框架函数来实现。您应该创建一个对象,其中包含 Widget 应该能够使用的所有功能,并将其传递给每个 Widget。例如:

var Api = {
   widgetApi = {
      someFunction: function(){
          // ...
      }
   },
   addWidget:function(){
      var temp = this.widgetApi.constructor(); 

      for(var key in this.widgetApi)
         temp[key] = clone(this.widgetApi[key]);
      return temp;
   }
   // Include other variables that Widgets can't use
}

这样,小部件可以执行功能并与主机或全局变量进行通信Api。要设置变量,小部件将编辑其私有对象,而不是全局对象。对于每一帧(代表一个 Widget),您必须初始化或创建对象的副本widgetApi,并且可能将其存储在一个数组中,这样一个 Widget 的实例就存储在主Api对象中。

例如,给定<iframe id="widget"></iframe>

您将执行以下操作:

var widget = document.getElementById("widget");
widget.contentWindow.Api = Api.addWidget();
widget.contentWindow.parent = null;
widget.contentWindow.top = null;

此外,在每一帧中,您都需要将parentandtop变量设置为 null,这样 Widget 就无法访问主帧的数据。我有一段时间没有测试过这个方法,所以可能有办法绕过将这些变量设置为空。

于 2012-10-13T02:43:52.987 回答