滚动我自己的要求

Rolling my own Require

前进

我注意到这个问题是 "Function Scoping - Not Block Scoping" 的答案,区别在于 for ... infor ... 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);
            }
        }
    }
})();

笨蛋

https://plnkr.co/edit/OFWTUpmV3sIJAjhGhcLO

这是旧的 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>

简而言之,您需要捕获 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);
            }
        }
    }
})();