如何在JS中引入外部文件函数?插件加载后window对象为空求解
动态加载插件后window对象未更新的原因及解决办法
问题描述
我正在为一个库开发插件系统,通过Ajax从其他文件获取函数,使用Function构造函数包装并返回该函数后添加到window对象中,但循环执行完成后检查window对象却发现它是空的。相关代码如下:
x.html (主文件)
<html> <head> <script src="//cdn.jsdelivr.net/npm/eruda"></script><script>eruda.init();</script> <script> window.addEventListener('error', function(e) { console.error(e, e.message, e.filename, e.lineno, e.colno, e.error); }); if (typeof window.example !== 'object') { window.example = { plugins: {} }; } </script> </head> <body> Text <script type="module"> import { loadPlugin } from "./y.js"; await loadPlugin(['bold','italic']); console.log(window.example.plugins) console.log(window.example.plugins.italic) </script> </body> </html>
y.js (导入器)
export async function loadPlugin(plugins, namespace) { for (var i = 0; i < plugins.length; i++) { if (typeof window.example.plugins[plugins[i]] === 'undefined') { fetch(`z.js`) .then((response) => response.text()) .then(function(code) { if (code.trim() !== '') { try { code += 'return theme;' window.example.plugins[plugins[i]] = (new Function(code))(); } catch (err) { console.log(err); } } }); } if (i === (plugins.length - 1)) { return new Promise(function(resolve) { resolve(window.example.plugins); }); } } }
z.js (待导入函数的文件,原标注为z.html需修正文件名)
function theme() { console.log('Hooray!'); }
问题原因
- 异步操作未等待,提前返回结果:
fetch是异步操作,循环中没有等待fetch的回调执行完成,就直接返回了window.example.plugins。此时插件还未加载完成,所以对象为空。 - var变量的作用域问题:循环中使用
var i,在fetch的then回调中,i会指向循环结束后的最终值(即2),导致所有插件都被错误地赋值到不存在的plugins[2]键上,而非预期的bold和italic。 - 文件路径不匹配:代码中
fetch的是z.js,但提供的待导入文件标注为z.html,会导致获取的内容不符合预期,执行Function时可能出错。
解决办法
修改后的y.js代码
export async function loadPlugin(plugins, namespace) { const promises = []; for (let i = 0; i < plugins.length; i++) { const pluginName = plugins[i]; if (typeof window.example.plugins[pluginName] === 'undefined') { // 收集每个fetch的Promise const loadPromise = fetch(`z.js`) .then(response => response.text()) .then(code => { if (code.trim()) { try { code += 'return theme;'; window.example.plugins[pluginName] = (new Function(code))(); } catch (err) { console.error(`加载插件${pluginName}失败:`, err); } } }) .catch(err => { console.error(`请求插件文件失败:`, err); }); promises.push(loadPromise); } } // 等待所有插件加载完成 await Promise.all(promises); return window.example.plugins; }
关键修改点
- 用
let声明循环变量,确保每个迭代的变量独立,避免作用域污染。 - 收集所有
fetch的Promise,通过Promise.all等待全部异步操作完成后再返回结果。 - 修正变量引用,直接使用
pluginName而非依赖循环变量i,避免回调中变量值不正确。 - 添加错误捕获,便于排查请求或执行过程中的问题。
- 确保待导入文件名为
z.js,与fetch的路径一致。
内容的提问来源于stack exchange,提问作者Codemaster United
相关产品推荐
相关产品推荐

