【问题标题】:Protecting a Global Javascript "API" Object保护全局 Javascript“API”对象
【发布时间】:2012-10-03 21:06:34
【问题描述】:

我目前有一个基于全局 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 var一切都存在,并破坏了整个应用程序?是否可以保护这个Api 变量不被外部覆盖?只允许“扩展”(添加新事物),但不允许“删除/更改”。即:

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 现在彼此分开。但是,如果我有许多 Widgets 正在运行,并且它们无法再与小部件的“主机”(框架所在的页面)通信,那么在一次又一次加载 Api 时会有额外的开销,例如,我可能想告诉宿主显示一个通知:Api.Notify.Show("Test"),但它不能这样做,因为这个Api完全独立于其他实例,它无法与“宿主”通信

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

  3. 1/2的混合物?

【问题讨论】:

  • 我不是 JS 大师——但我认为仅靠 JS 是无法做到的。也许看看 MS 的新“TypeScript”,它有更好的语言特性,可以编译成 JS,也许这可以让你保护它。
  • 是的,我不认为有任何万无一失的方法可以避免全局被第三方脚本(甚至是您自己的脚本)破坏,但有一件事我会建议为您的对象使用不太可能被其他脚本使用的名称。 var TFQ = {} 可能比Api 更不容易发生冲突。
  • 你可以试试这个:stackoverflow.com/questions/1759987/…。也许它有帮助..;.

标签: javascript html object


【解决方案1】:

看看

Object.freeze

更多信息here

这是来自 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

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

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

【讨论】:

  • 显然这保护了对象,但不保护修改它,所以这不会是问题的完整解决方案,除了@Kernel James 的答案之外,这可能是您正在寻找的
【解决方案2】:

将其设为constant

const Api = "hi";

Api = 0;

alert(Api); //"hi"

【讨论】:

  • 不错,比 Object.freeze 好,这应该是公认的答案!
【解决方案3】:

没有防止“第三方”小部件覆盖全局变量的好方法。通常,组装最终应用程序的人有责任确保他们使用的任何 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 一样。

【讨论】:

    【解决方案4】:

    保护全局变量的唯一方法(至少我能想到的)是防止小部件直接访问它。正如您所建议的,这可以通过使用框架函数来实现。您应该创建一个对象,其中包含 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进行通信。要设置变量,小部件将编辑其私有对象,而不是全局对象。对于每一帧(代表一个小部件),您必须初始化或创建widgetApi 对象的副本,并可能将其存储在一个数组中,这样一个实例 存储在主 Api 对象中。

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

    您将执行以下操作:

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

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

    【讨论】:

    • 谢谢!但是,小部件如何与主机通信?我如何“观看” Api.widgetsApi["widget"] 以获取新的“消息”?
    • @JimmieLin:widgetApi 中的函数仍然可以与Api 对象交互。因此,您可以调用其他函数并从中编辑变量。例如,如果您有Api.widgetApi.Debug.Messages.push(),您可以将其链接到全局Api 内的push 函数。
    • 所以本质上,小部件内的Api.widgetApi.Debug.Messages.push() 相当于它们外的Api.Debug.Messages.push()?听起来很酷,谢谢!
    • 只要你在widgetApi里面有一个函数调用它外面的函数(this.Debug.Messages.push())
    猜你喜欢
    • 2013-12-10
    • 2014-08-27
    • 2014-10-14
    • 1970-01-01
    • 2012-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多