滚动我自己的要求

时间:2016-01-13 15:42:16

标签: javascript class require

转发

我注意到这个问题是一个&#34;功能范围 - 不阻止范围&#34;回答,差异是for ... in反对for ... i<n ... i++解决方案可能是使用函数包装for(var p in ...) {以赋予它们自己的范围(就像使用Array.prototype.forEach一样)。谢谢您的帮助。

问题

我正在使用我自己的小型Require.js解决方案,而不是实际的Require.js库(不,我没有看过他们的来源)。

我的回调函数似乎永远不会执行,但我无法弄清楚原因。这可能是一个简单的逻辑错误,但是当你长时间盯着自己的代码时,你知道它是怎么回事。 (一切看起来都很合理)

用法

使用如下:

require(["LibraryA", "LibraryB", "LibraryC"], function() {
    //code which requires libraries a-c here
});

代码

var require = (function() {
    var scriptsLoaded = [];
    return function(paths, callback) {
        var pathsDoneLoading = {};
        for(var i = 0; i < paths.length; i++) {
            pathsDoneLoading[paths[i]] = false;
        }
        for(var p in pathsDoneLoading) {
            for(var i = 0; i < scriptsLoaded.length; i++) {
                if(p === scriptsLoaded[i]) {
                    pathsDoneLoading[p] = true;
                }
            }
        }
        for(var p in pathsDoneLoading) {
            if(pathsDoneLoading[p] === false) {
                var script = document.createElement("script");
                script.src = p + ".js";
                script.onload = function() {
                    scriptsLoaded.push(p);
                    pathsDoneLoading[p] = true;
                    for(var p in pathsDoneLoading) {
                        if(pathsDoneLoading[p] !== true) {
                            return;
                        }
                    }
                    callback();
                };
                document.documentElement.appendChild(script);
            }
        }
    }
})();

Plunker

https://plnkr.co/edit/OFWTUpmV3sIJAjhGhcLO

2 个答案:

答案 0 :(得分:2)

这是旧的for循环变量捕获问题。

在循环中,它正在迭代加载的路径(for (var p in pathsDoneLoading) {),p被绑定到里面的函数。问题是p在循环完成后会发生变化并持续存在,因此每个函数都会将pathsDoneLoading对象中的 last 键添加到scriptsLoaded数组中(并将pathsDoneLoading中的匹配值设置为true)。

问题的一个例子:

var obj = { a: 1, b: 2, c: 3 };
var funcs = [];
for (var key in obj) {
  funcs.push(function() {
    return key; // Remember, `key` is bound to the function, not the block
  });
}

funcs.forEach(function(f) {
  document.querySelector('pre').innerText += f() + '\n';
});
<pre></pre>

要解决此问题,您可以将其包装在IIFE中,以便按p值创建新的绑定。

for (var p in pathsDoneLoading) {
  (function(p) {
    if (pathsDoneLoading[p] === false) { // or `if (!pathsDoneLoading[p])`
    ...
  })(p);
}

这样每次生成onload处理程序时,它们都具有与预期路径的唯一绑定。

我们之前的示例已使用此解决方案修复:

var obj = { a: 1, b: 2, c: 3 };
var funcs = [];
for (var key in obj) {
  (function(key) {
    funcs.push(function() {
      return key; // Remember, `key` is bound to the function, not the block
    });
  })(key);
}

funcs.forEach(function(f) {
  document.querySelector('pre').innerText += f() + '\n';
});
<pre></pre>

答案 1 :(得分:0)

简单地说,你需要捕获for循环迭代器,因为它是一个移动的目标,并且当onload触发时,它会发生变化。

    var require = (function() {
    var scriptsLoaded = [];
    return function(paths, callback) {
        var pathsDoneLoading = {};
        for(var i = 0; i < paths.length; i++) {
            pathsDoneLoading[paths[i]] = false;
        }
        for(var p in pathsDoneLoading) {
            for(var i = 0; i < scriptsLoaded.length; i++) {
                if(p === scriptsLoaded[i]) {
                    pathsDoneLoading[p] = true;
                }
            }
        }
        for(var p in pathsDoneLoading) {
            if(pathsDoneLoading[p] === false) {
                var script = document.createElement("script");
                script.src = p + ".js";

                // Wrapping in a closure
                script.onload = (function(Vp) {
                    scriptsLoaded.push(Vp);
                    pathsDoneLoading[Vp] = true;
                    for(var prop in pathsDoneLoading) {
                        if(pathsDoneLoading[prop] !== true) {
                            return;
                        }
                    }
                    callback();

                // Execute the closure function and send in your 
                // var(s) as argument(s) to capture it's current state
                }(p));
                document.documentElement.appendChild(script);
            }
        }
    }
})();