动态脚本加载同步

时间:2009-04-21 21:39:21

标签: javascript

我有一个脚本知道加载包含javascript类的动态脚本。 我正在使用以下代码加载类脚本:

var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "myscript.js";
head.appendChild(script);

然后我尝试使用eval创建新类:

var classObj = eval(" new MyClass()" );

问题是eval的代码正在执行,脚本已被加载到内存中,我收到MyClass is undefined.

的错误

有没有办法同步这些事件?我需要确保脚本完全加载到内存中才能开始从中分配类。

6 个答案:

答案 0 :(得分:5)

您需要在onload方法,符合Web标准的浏览器或onreadystatechange中附加事件处理程序,在Internet Explorer中检查script.readyState属性是否等于“loaded”或“complete”。 / p>

在收到加载脚本的通知之前,您可能正在尝试访问尚未声明或创建的对象,函数和属性。

以下是一个示例函数,摘自bezen.dom.js中的模块Javascript library, bezen.org

var appendScript = function(parent, scriptElt, listener) {
    // append a script element as last child in parent and configure 
    // provided listener function for the script load event
    //
    // params:
    //   parent - (DOM element) (!nil) the parent node to append the script to
    //   scriptElt - (DOM element) (!nil) a new script element 
    //   listener - (function) (!nil) listener function for script load event
    //
    // Notes:
    //   - in IE, the load event is simulated by setting an intermediate 
    //     listener to onreadystate which filters events and fires the
    //     callback just once when the state is "loaded" or "complete"
    //
    //   - Opera supports both readyState and onload, but does not behave in
    //     the exact same way as IE for readyState, e.g. "loaded" may be
    //     reached before the script runs.

    var safelistener = catchError(listener,'script.onload');

    // Opera has readyState too, but does not behave in a consistent way
    if (scriptElt.readyState && scriptElt.onload!==null) {
      // IE only (onload===undefined) not Opera (onload===null)
      scriptElt.onreadystatechange = function() {
        if ( scriptElt.readyState === "loaded" || 
             scriptElt.readyState === "complete" ) {
          // Avoid memory leaks (and duplicate call to callback) in IE
          scriptElt.onreadystatechange = null;
          safelistener();
        }
      };
    } else {
      // other browsers (DOM Level 0)
      scriptElt.onload = safelistener;
    }
    parent.appendChild( scriptElt );
};

为了使其适应您的需要,您可以替换对catchError的调用,该调用包装侦听器以捕获并记录错误,并使用修改后的函数:

var appendScript = function(parent, scriptElt, listener) {
    // append a script element as last child in parent and configure 
    // provided listener function for the script load event
    //
    // params:
    //   parent - (DOM element) (!nil) the parent node to append the script to
    //   scriptElt - (DOM element) (!nil) a new script element 
    //   listener - (function) (!nil) listener function for script load event
    //
    // Notes:
    //   - in IE, the load event is simulated by setting an intermediate 
    //     listener to onreadystate which filters events and fires the
    //     callback just once when the state is "loaded" or "complete"
    //
    //   - Opera supports both readyState and onload, but does not behave in
    //     the exact same way as IE for readyState, e.g. "loaded" may be
    //     reached before the script runs.

    var safelistener = function(){
      try {
        listener();
      } catch(e) {
        // do something with the error
      }
    };

    // Opera has readyState too, but does not behave in a consistent way
    if (scriptElt.readyState && scriptElt.onload!==null) {
      // IE only (onload===undefined) not Opera (onload===null)
      scriptElt.onreadystatechange = function() {
        if ( scriptElt.readyState === "loaded" || 
             scriptElt.readyState === "complete" ) {
          // Avoid memory leaks (and duplicate call to callback) in IE
          scriptElt.onreadystatechange = null;
          safelistener();
        }
      };
    } else {
      // other browsers (DOM Level 0)
      scriptElt.onload = safelistener;
    }
    parent.appendChild( scriptElt );
};

答案 1 :(得分:2)

由于您似乎能够编辑外部脚本(因为您使用警报对其进行了测试),为什么不将此代码放在该脚本中呢?

如果你不能这样做(也许生成额外的代码或者可能共享第一个文件),只需在你加载的脚本末尾添加一个函数调用,如下所示:

load_complete();

然后将额外的代码放在该函数中:

function load_complete() {
    var classObj = eval(" new MyClass()" );
}

它比任何类型的onload触发器都简单和万无一失。此外,如果共享js文件,那么您可以在每个使用它的页面上使用不同的load_complete函数(只需确保始终定义load_complete,即使它是空的)。

答案 2 :(得分:1)

使用jQuery(JavaScript库)getScript function to load your script async. Use the callback function to create your objects. Example:

$.getScript("script.js", function () {
  var classObj = new MyClass();
});

答案 3 :(得分:1)

我相信这实际上可以通过确保您使用外部脚本加载外部脚本和代码的代码来解决这个问题:

<script>
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "myscript.js";
head.appendChild(script);

//this is in the same script block so it wouldn't work
//var classObj = eval(" new MyClass()" );
</script>

<script>
//this is in a separate script block so it will work
var classObj = eval(" new MyClass()" );
</script>

答案 4 :(得分:0)

您确定向DOM添加<script>元素会导致浏览器实际评估脚本吗?我有一种模糊的记忆,在某处看不到,但也许我昨天吸了太多的烤箱清洁剂。

答案 5 :(得分:0)

Amir,它看起来好像脚本仍然在服务器上,也就是说,浏览器没有加载。因此,head.appendChild(script);附加了 null 。您无法通过说出其名称来获取服务器中的脚本,您需要请求它并将其注入页面,使用ajax或使用<script>标记加载它。