HOWTO · JavaScript

如何从另一个 JS 文件导入函数

了解如何使用浏览器脚本、ECMAScript 模块、Node.js CommonJS 或传统的 jQuery,从另一个 JavaScript 文件调用函数。

本页内容

要从另一个 JavaScript 文件调用函数,请根据环境选择共享方法:经典的 <script> 标签将函数公开为浏览器全局变量,ECMAScript 模块使用 export 和 import,Node.js CommonJS 使用 module.exports 和 require。下面的示例展示了如何连接这些文件以及每种方法的运行位置。

在浏览器中从另一个 JavaScript 文件调用函数

对于使用经典脚本的页面,在一个文件中定义函数,并在调用它的脚本之前加载该文件。经典脚本共享页面的全局作用域,因此第一个文件可以将函数附加到 window:

  • 在 greeting.js 中,编写 window.showGreeting = function () { document.querySelector("#message").textContent = "Hello from greeting.js"; };。
  • 在 app.js 中,编写 document.querySelector("#show").addEventListener("click", window.showGreeting);。

将这些文件保存在 index.html 旁边。此页面按所需顺序加载它们,并提供按钮和消息元素:

<!doctype html>
<html lang="en">
  <meta charset="utf-8">
  <title>Call a function from another file</title>
  <p id="message">Click the button to show the message.</p>
  <button id="show" type="button">Show greeting</button>
  <script src="greeting.js"></script>
  <script src="app.js"></script>
</html>

当按钮被点击时,app.js 中的处理程序会调用从 greeting.js 加载的函数。请将定义脚本放在前面;否则,页面可能会在 window.showGreeting 存在之前尝试注册处理程序。这个 html 代码块是浏览器页面示例,而不是运行时输出记录。

使用 ECMAScript 模块共享函数

当您希望使用显式导入而不是共享全局变量时,请使用 ECMAScript 模块。将这三个完整文件保存在同一目录中。第一个文件导出一个函数,第二个文件导入它并更新页面:

  • greeting.js:export function makeGreeting(name) { return \Hello, ${name}!`; }`
  • app.js:import { makeGreeting } from "./greeting.js"; document.querySelector("#message").textContent = makeGreeting("Ada");

将此完整页面另存为 index.html,放在这些文件旁边。在模块运行之前,其消息元素已经存在:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Share a function with a module</title>
  </head>
  <body>
    <p id="message">The greeting will appear here.</p>
    <script type="module" src="app.js"></script>
  </body>
</html>

保持相对导入路径与文件位置一致。浏览器以模块语义加载模块,不会将其中声明的内容放到 window 上。

请通过本地开发服务器以 HTTP 方式提供该页面;直接以 file: URL 打开它通常会阻止模块加载。这是浏览器的托管要求,而不是运行 Node.js 的要求。Node.js 也支持 ECMAScript 模块,并有其自己的包和文件扩展名规则。ECMAScript 模块导出语法对函数和其他值使用同样的 export 机制。

使用 CommonJS 在 Node.js 中导入函数

CommonJS 是 Node.js 传统的模块系统。模块可以将函数赋给 module.exports,另一个文件可以用 require 获取它。链接的 JavaScript import 与 require 的比较解释了这些模块系统之间的差异。

这个自包含的 Node.js 示例创建一个辅助文件,导入其函数并调用它。将代码保存为 main.cjs,并在安装了 Node.js 的终端中运行 node main.cjs:

const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");

const folder = fs.mkdtempSync(path.join(os.tmpdir(), "function-demo-"));
try {
  const helperPath = path.join(folder, "math.cjs");
  fs.writeFileSync(helperPath, "module.exports = (a, b) => a + b;\n");
  const add = require(helperPath);
  console.log(`Result: ${add(5, 3)}`);
} finally {
  fs.rmSync(folder, { recursive: true, force: true });
}
Result: 8

辅助文件导出一个函数,调用方将其作为 require 的返回值接收;最后的 console.log 调用使用这个返回的函数。.cjs 扩展名使辅助文件的 CommonJS 格式明确,即使周围项目使用 ECMAScript 模块也是如此。此示例是为 Node.js 编写的,不会在浏览器页面中运行。

使用 jQuery 加载 JavaScript 文件

jQuery 的 $.getScript() 可以按需加载脚本。这是已经使用 jQuery 的页面的一种传统替代方法;新代码通常使用浏览器模块或普通脚本标签会更清晰。在调用方之前加载 jQuery,并等待加载回调后再使用下载文件中的函数。

例如,mathUtils.js 可以使用 window.mathUtils = { add: (a, b) => a + b }; 公开一个浏览器全局变量。在页面中,先加载 jQuery,并使用回调在脚本加载后调用函数:

<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script>
  $.getScript("mathUtils.js")
    .done(function () {
      const result = window.mathUtils.add(5, 3);
      $("body").append(`<p>Result: ${result}</p>`);
    })
    .fail(function () {
      $("body").append("<p>The script could not be loaded.</p>");
    });
</script>

请求是异步的,因此依赖 mathUtils 的代码应放在 .done() 内部(或其他完成处理程序中),而不是紧跟在 $.getScript() 之后。对于跨源 URL,jQuery 可以通过脚本元素加载脚本;浏览器通常不要求对此类脚本加载进行 CORS。跨源的 fetch() 或 XHR 请求则确实需要服务器用 CORS 允许。脚本加载仍可能被 Content Security Policy 等浏览器策略阻止,或者在 URL 无法访问时失败。有关其加载行为,请参阅 jQuery $.getScript() 文档。