滚动我自己的要求
Rolling my own Require
前进
我注意到这个问题是 "Function Scoping - Not Block Scoping" 的答案,区别在于 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);
}
}
}
})();
笨蛋
这是旧的 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);
}
}
}
})();
前进
我注意到这个问题是 "Function Scoping - Not Block Scoping" 的答案,区别在于 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);
}
}
}
})();
笨蛋
这是旧的 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);
}
}
}
})();