通过iframe脚本加载器避免全局变量的污染?

时间:2010-09-30 11:33:40

标签: javascript iframe global-variables

问题...

存在编码不良的脚本,需要包含在网页中。

这些脚本通过以下方式污染全局范围:

  • 将值分配给未声明的标识符
  • 向内置构造函数添加属性(如ObjectArray)及其原型
  • 其他令人讨厌的东西。

解?

我希望包含脚本而不会产生不良副作用。我认为可以通过在iframe中加载脚本并将对象导出为父窗口的属性来实现。这是我到目前为止所得到的:

<script>

(function(){
  var g=this, frameIndex=frames.length, f=document.createElement('iframe');

  // hide it like this instead of display:none, because some old browser ignores
  // iframes with display:none, or is this an ancient habit I can drop?
  f.style.width='0px'; f.style.height='0px'; 
  f.style.border='none'; f.style.position='absolute';

  // append it to document.body or document.documentElement?
  // documentElement seems to work before body is loaded,
  // but is it cross-browser safe?  
  document.body.appendChild(f);

  // window object for our iframe
  var w=frames[frameIndex];

  // callback function pulls the object into the current window when script loads
  w.cb=function(){ g.SomeObject=w.SomeObject };

  // will this work on IE, or do I need to use document.createElement?
  // wanted to avoid document.createElement in this case because I'm not sure 
  // whether to call it from window.document or frames[frameIndex].document
  w.document.innerHTML='<script onload="cb()" src="myscript.js"><\/script>';

}());

</script>

问题:

  • 如果脚本修改了内置原型并将其移动到另一个窗口,或者我父窗口的内置插件是否保持干净并且一切都“正常工作?”

    <是否存在潜在的破坏? / LI>
  • 这个想法是否适用于'大多数'浏览器,还是有一个显示阻止?到目前为止,还没有对chrome和moz之外的任何东西进行过测试。

  • 我想在将对象拉入当前窗口后删除iframe,但如果删除iframe,moz将丢失对象引用。有没有人知道如何解决这个问题?

  • 这已经完成,还是有更好的方法来实现我的目标?如果是这样,我应该寻找的脚本或技术的名称是什么?

(问题移植自here

4 个答案:

答案 0 :(得分:1)

要复制一个函数,你可以把它转换成一个字符串然后评估它....下面的代码也证明了这样做后iframe可以删除,你的副本保持不变。

使用FF的以下代码示例

Child.html代码段

<script>

//
// modify the prototype
//
Object.prototype.test = function(msg)
{
        alert(msg);
};  

//
// Simply declare a function
//
var whoo_hoo = function(){alert("whoo hoo");}
</script>

使用iframe的父级:

 <iframe id="help_frame" src="http://localhost/child.html"
 onLoad="javascript:Help.import_functions(this)"></iframe>

    <script>
    var Help = {

          imported_function :null,
              import_functions : function(iframe)
   {
    this.imported_function = String(iframe.contentWindow.whoo_hoo);
    eval("this.imported_function = " + this.imported_function);
    iframe.parentNode.removeChild(iframe);

   //
   // displays 'whoo hoo' in an alert box
   //
   this.imported_function();

   try
   {
      //
      // If the Object prototype was changed in the parent
      // this would have displayed 'should not work' in an alert
      //
      this.test('should not work');
   }
   catch(e){alert('object prototype is unmodified');}

   }, 
    </script>

http://thecodeabode.blogspot.com/

答案 1 :(得分:1)

这可能是一个可能的解决方案:

  • 将所有外来代码打包成一个类
  • 制作该类的所有未声明标识符成员
  • 在调用凌乱的代码之前,制作一个内置类的副本并以不同的方式命名它们 (这可能吗?)。

我认为这应该解决所有问题。

根据我的建议,你的样本

var badA = "hahaha";
this.badB = "hehehe";
badC = "hohoho";

String.prototype.star = function(){ return '***' + this + '***' }

var somethingUseful = {
  doStuff: function () {
    alert((badA + badB + badC).star());
  }
}

应该是这样的

// Identifies the added properties to prototypes (ie String and Function)
// for later filtering if you need a for-in loop.
var stringAddons = [];
var functionAddons = []
var _string = new String();
var _function = function() {};
for (var property in _string) { if (!_string.hasOwnProperty(property)) { stringAddons.push(property); }}
for (var property in _function) { if (!_function.hasOwnProperty(property)) { functionAddons.push(property); }}

// Wraps the undeclared identifiers
var global = function()
{
  this.badA = "hahaha";
  this.badB = "hehehe";
  this.badC = "hohoho";

  String.prototype.star = function(){ return '***' + this + '***' }

  this.somethingUseful = {
    doStuff: function () {
      alert((global.badA + global.badB + global.badC).star());
    }
  }
}
var global = new Global();
global.somethingUseful.doStuff();

棘手的部分是制作所有未声明的标识符全局属性。 也许一个好的正则表达式脚本可以成功。我对正则表达式不太好:)。

答案 2 :(得分:1)

根据加布里埃尔的回答发表评论的代码..

var r = {
    init : null,
    _init: function(){
        var obj = new XMLHttpRequest();
        obj.onreadystatechange = function(){
            if ((this.status == 200) && this.readyState==4){
                try{
                    eval("r.init = function(){" + this.responseText + "}");
                    r.init();
                } catch(e){/*something bad in the script...*/}
            }
        }
        obj.open("GET","/jspolute_bad.js", true);
        obj.send();
    }   
}
r._init();

如果将方法添加到原型中,如果一个或两个导出的函数在外部代码中修改该方法,则可能会遇到麻烦。我想到的繁琐的解决方案是在为array.prototype,string.prototype评估它之前使用Regex的responseText并修复它。将尝试并让你知道..但它主要迎合简单的脚本。

答案 3 :(得分:0)

到目前为止,答案似乎都没有像iframe一样好用。我相信iframe监狱将成为问题的解决方案。我现在把我现有的解决方案作为答案,因为它似乎比目前提供的其他答案更好。我真的很想将这种iframe技术改进成万无一失的技术,我很想了解修改过的原型是如何工作的。此示例适用于chrome和moz。

test.js

var badA = "hahaha";
this.badB = "hehehe";
badC = "hohoho";

String.prototype.star = function(){ return '***' + this + '***' }

var somethingUseful = {
  doStuff: function () {
    alert((badA + badB + badC).star());
  }

}

的test.html

<html>
  <head>
    <script>
    /** 
      safeLoad - load a script in an iframe jail

      @param {String} scriptPath    path to a javascript file
      @param {String} target        name of global object to import
      @param {Function} callback    function to execute after script loads
    */ 
    function safeLoad (scriptPath, target, callback) {
      var g=this, f=document.createElement('iframe'), frameIndex=frames.length;

      f.style.width='0px'; 
      f.style.height='0px'; 
      f.style.border='none'; 
      f.style.position='absolute';

      f.onload=function(){
        var w=frames[frameIndex];
        var s=w.document.createElement('script');
        s.src=scriptPath;
        s.onload=function(){
          g[target]=w[target];
          if (callback && callback.apply) callback(w);
        };
        w.document.body.appendChild(s);
      }
      document.documentElement.appendChild(f);
    }
    </script>  

    <script>

    safeLoad('test.js', 'somethingUseful', function init () {
      // next line should give ***hahahahehehehohoho***
      somethingUseful.doStuff();
      // next line should give undefinedundefinedundefinedundefined
      alert(typeof badA + typeof badB + typeof badC + String.prototype.star);
    });

    </script>   
  </head>
</html>