代码之家  ›  专栏  ›  技术社区  ›  Dagg Nabbit

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

  •  7
  • Dagg Nabbit  · 技术社区  · 15 年前

    问题

    存在需要包含在网页上的编码不好的脚本。

    • 为未声明的标识符赋值
    • Object Array )以及他们的原型
    • 其他讨厌的东西。

    解决方案

    我想包括没有副作用的脚本。我认为可以通过在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>
    

    问题:

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

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

    (问题源自 here )

    4 回复  |  直到 8 年前
        1
  •  1
  •   Ben    15 年前

    要复制函数,可以将其强制转换为字符串,然后对其求值。。。。下面的代码还演示了执行此操作后可以删除iframe,并且您的副本保持完整。

    下面是使用FF的代码示例

    <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/

        2
  •  1
  •   Ravindra Sane    15 年前

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

    将方法添加到prototype中后,如果一个或两个导出的函数在外部代码中修改时需要该方法,则可能会遇到麻烦。想到的冗长的解决方案是在为array.prototype、string.prototype评估responseText之前对其进行正则化,并以某种方式进行修复。我会试试看,让你知道。。但它只会迎合简单的脚本。

        3
  •  1
  •   Gabriel    15 年前

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

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

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

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

    棘手的部分是制作所有未声明的标识符 全球的 财产。 也许一个好的正则表达式脚本可以做到。我不太擅长正则表达式:)

        4
  •  0
  •   Dagg Nabbit    15 年前

    到目前为止,没有一个答案像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>