【问题标题】:dynamic script loading synchronization动态脚本加载同步
【发布时间】:2010-10-20 23:00:43
【问题描述】:

我有一个知道动态加载包含 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.

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

【问题讨论】:

标签: javascript


【解决方案1】:

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

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

【讨论】:

  • 这是不可靠的,根据getScript() 文档:“一旦加载脚本但不一定执行回调就会触发。”
【解决方案2】:

我相信这实际上可以通过确保将加载外部脚本的代码和使用外部脚本的代码分开的脚本块来解决,如下所示:

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

【讨论】:

    【解决方案3】:

    既然您似乎能够编辑外部脚本(因为您使用警报对其进行了测试),为什么不把这段代码放在那个脚本中呢?

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

    load_complete();
    

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

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

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

    【讨论】:

      【解决方案4】:

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

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

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

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

      【讨论】:

        【解决方案5】:

        Amir,在我看来,脚本似乎仍然位于服务器上,也就是说,还没有被浏览器加载。所以你的head.appendChild(script);是附加null。您不能仅通过说出其名称来从服务器获取脚本,您需要使用 ajax 或通过使用 &lt;script&gt; 标记将其加载到页面中来请求它并将其注入页面。

        【讨论】:

        • 一开始我也是这么想的。我对其进行了调试,发现脚本在几毫秒后变得可用。我添加了一个警报(“我已加载”);进入脚本并看到它执行。
        • -1 它立即附加脚本 element,但它异步加载和执行脚本 code,这就是该类不能立即使用的原因.
        【解决方案6】:

        您确定将&lt;script&gt; 元素添加到DOM 会导致浏览器真正评估脚本吗?我有一个模糊的记忆,在某个地方没有读到,但也许我昨天吸入了太多的烤箱清洁剂。

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-10
        • 2010-10-04
        • 2015-01-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多