在 CONSTRUCT 中学习 JAVASCRIPT,第 12 部分:模块

2,406次阅读
没有评论

共计 6201 个字符,预计需要花费 16 分钟才能阅读完成。

在 CONSTRUCT 中学习 JAVASCRIPT,第 12 部分:模块

这是教程系列的第 12 部分在 Construct 中学习 JavaScript. 本部分从第 11 部分开始继续。因此,如果您错过了它,请参阅在 Construct 中学习 JavaScript,第 11 部分:构造 API.

在本指南中,我们介绍了 JavaScript 的许多语言基础知识,以及该语言、浏览器和 Construct 中的一系列内置功能。我们现在即将完成本指南,只有最后一个主要主题要介绍:模块。同样,我们在这里可能会介绍很多东西,但我们将介绍这个概念,介绍基础知识,并描述一些 Construct 独有的方面。特别是,由于事件表中的脚本是 Construct 独有的概念,因此有一种特定的处理方式。

在更广泛的 JavaScript 生态系统中,有各种不同的方法来处理模块。但是,在本指南中,我们将处理 JavaScript 语言中内置的模块系统,有时也称为 ES 模块。大多数其他模块系统都早于此,从长远来看,更广泛的 JavaScript 生态系统可能会朝着在任何地方使用 ES 模块的方向发展。

模块

请记住,顶级变量和函数的范围限定为文件。这意味着您可以在该脚本文件的任何位置使用它们,但不能在另一个脚本文件中使用它们。

跨文件使用事物的一种方法是使它们成为全局的,并将它们添加为 的属性。但是,这存在许多问题。它很快就会变得杂乱无章,因为您最终可能会在一个地方获得数百甚至数千个功能。此外,它可能会导致名称冲突:如果您想使用两个不同的库,并且它们都尝试定义具有相同名称的全局函数,则一个将覆盖另一个,并且可能会破坏某些内容。globalThis

模块通过提供一个干净且有序的界面来在 JavaScript 文件之间共享内容,从而解决了这些问题。每个文件都是一个独立的模块 – 其顶级变量和函数不能在文件外部访问。另一种说法是它的内容对模块是私有的。如果要公开要在文件外部使用的内容,则必须将其与关键字一起导出。然后,另一个文件必须与关键字一起导入它。exportimport

在这一点上,这可能感觉有些做作,因为到目前为止,我们所有的示例都涉及少量的变量和函数。为什么需要将它们分布在不同的文件中?对于简单的代码,将所有内容保存在一个文件中确实很容易。但是,当您开始编写更多代码时,快速组织多个文件的过程变得非常重要。否则,您最终可能会得到难以添加、更改或修复的混乱代码。了解模块将有助于确保在开始编写大量代码时可以保持代码井然有序。它对于重用代码和集成库也很有用,这些库可能用作模块。

简单的导出

在此示例中,我们使用一个包含两个脚本文件的新空项目:main.js 和 mymodule.js。我们将从 mymodule.js 导出一些东西,并在 main.js 中使用它。下载并打开下面的项目文件,以进行此示例。

mymodule.js 打开文件。在下面添加仅返回一个字符串的函数。

function GetMessage()
{
return "Hello world from mymodule.js!";
}
目前,此函数不能在此文件之外使用。但是,如果我们添加关键字,它就可用于其他文件进行导入。这将在函数的开头进行,如下所示。export

export function GetMessage()
{
return "Hello world from mymodule.js!";
}
脚本文件可以根据需要导出任意数量的内容。在本例中,我们只是导出一个函数。

现在开放 main.js。我们想打电话,但它仍然不可用。要从 mymodule.js 导入所有内容,请添加以下行:GetMessage()

import as MyModule from "./mymodule.js";
表示“一切”,表示将所有内容放在名为 的对象中,并指示要加载的文件。因此,总的来说,这意味着“将所有内容导入到以 mymodule.js 命名的对象”。
as MyModuleMyModulefrom "./mymodule.js"MyModule

请注意,文件路径以 开头。这实际上是必需的,并表明它是一个文件(因为可以从其他位置或以其他方式导入内容)。"./mymodule.js"./
现在,从 mymodule.js 导出的所有内容都位于名为 的对象中。所以我们的函数可以用 调用。请尝试以下代码 main.js 将消息记录到控制台。MyModuleGetMessage()MyModule.GetMessage()

import * as MyModule from "./mymodule.js";

console.log(MyModule.GetMessage());
预览项目并检查浏览器控制台,您应该会看到出现的消息!

恭喜 – 您的第一个导入已正常工作。

一些额外的注意事项

以下是有关模块工作原理的一些额外信息。

主脚本

构造本身只会加载并运行 main.js。如果在“项目栏”中选择脚本文件,则会在“属性栏”中看到其属性。这些属性包括 Purpose,该 Purpose 设置为 main.js 的 Main 脚本。

一个项目只能有一个主脚本。如果要加载和使用所有其他脚本,则必须导入它们,因为 Construct 不会自动加载它们。(请注意,mymodule.js 的目的为“未设置”。

主脚本也在项目栏中以粗体突出显示,因此您可以轻松查看哪个脚本被设置为主脚本。

重复导入同一文件

如果您有多个文件都导入 mymodule.js,它仍然只加载并运行脚本一次。第一次实际上会加载它,所有后续时间都会重用第一次。这确保了模块的高效工作。

导入名称

请注意,从 mymodule.js 导出的内容被导入到我们命名为 MyModule 的对象中。这避免了使它们都成为全局函数或顶级函数,这将具有前面提到的各种陷阱。它使一切井井有条,意味着您可以轻松地使用来自两个不同模块的同名的两个函数。

在编程中,术语命名空间是指使用名称的位置,例如脚本文件的顶层,或者在 MyModule 等对象内。将内容保留在不同的命名空间中可以避免出现诸如两个同名函数相互覆盖等问题。

如果需要,有一些方法可以将内容导入到顶级范围,甚至可以使用不同的名称导入它们。但是,由于您可以按照自己的喜好编写 import 语句,因此您始终可以控制导入的所有内容使用哪些名称。

导入和导出是静态的

导出只能显示在脚本的顶层。你不能做一些事情,比如在 ’if’ 语句中导出一些东西。这强制要求模块必须始终导出相同的事物集。

import 语句必须出现在脚本的顶部,然后出现在任何其他代码行之前。这也意味着您不能在 ‘if’ 语句中使用 import 语句(尽管存在用于该用途的单独动态导入功能)。

这些规则旨在允许工具执行捆绑和优化代码等操作。它还有助于保持事情的一致性和可预测性。

导入和导出

以下是您可以使用 and 关键字执行的其他一些操作。这并不全面,但是一个有用的总结。importexport

导入特定功能

重用我们之前的示例,该函数也可以像这样导入和使用:GetMessage()

import {GetMessage} from "./mymodule.js";

console.log(GetMessage());
在此示例中,表示仅导入从 mymodule.js 导出的函数。它也被设为顶级函数,因此被称为 just alone; 没有名为 的对象。import {GetMessage} from "./mymodule.js";GetMessageGetMessage()MyModule

可以使用表单导入多个内容:import {FuncA, FuncB, FuncC} from "./file.js";

原始示例导入了所有内容并为其命名。如果您知道只需要导入一些特定内容,而不需要绝对所有内容,则此方法非常有用。

导入脚本只是为了运行它

您也可以使用来仅加载和运行脚本文件。它不会导入任何东西或以任何方式使用其导出。import "./file.js";

这对于不导出任何内容而只添加全局变量的旧库非常有用。在这种情况下,在导入脚本后,您可以访问它添加到的任何内容。或者,脚本可能仅通过运行其顶级代码来执行一些有用的操作,在这种情况下,您无需导入任何内容,因为只需加载和运行脚本就足够了。globalThis

默认导出

如果脚本只有一个导出,或者有一个您大部分时间都想导入的主要内容,则可以将其设为默认导出。这意味着它是使用 导出的,而不仅仅是。请尝试下面的代码进行 mymodule.js。export defaultexport

export default function GetMessage()
{
return "Hello world from mymodule.js!";
}
反过来,默认导出的导入方式略有不同。请尝试下面的代码进行 main.js。

import GetMessage from "./mymodule.js";

console.log(GetMessage());
现在预览项目,消息将被记录下来。

在这种情况下,意味着从 file.js 导入默认导出并为其命名 Thing。请注意,在上一个示例中,这不使用大括号 and(导入具有特定名称的导出)或 or 部分(导入所有内容)。import Thing from "./file.js"{}*as

不要忘记,以这种方式导入的脚本必须指定默认导出,否则将收到错误。

导入是只读的

您导入的所有内容都是只读的,因为其中无法重新分配。这有助于确保模块始终公开一致的接口。

但是,有时这可能会带来一些问题。例如,如果要导出如下所示的变量:

export let myVariable = 2;
并像这样导入它:

import {myVariable} from "./mymodule.js";

// TypeError: Assignment to constant variable.
myVariable = 3;
… 那么在 之后,你就不能改变 的值了。即使我们导出了用 声明的变量,它仍然表现得好像它真的是。importmyVariableletconst

解决此问题的一种方法是使用对象。对象本身不能重新分配,但其属性可以。因此,我们可以使用对象属性而不是变量。以下示例展示了如何将其用作在模块中存储相关变量组的一种方式。在 mymodule.js 使用中:

export default {
score: 100,
lives: 3,
ammo: 50
};
这使用具有分数、生命和弹药属性的对象的默认导出。

然后在 main.js 使用中:

import GameVariables from "./mymodule.js";

GameVariables.score += 100;

console.log(Score: ${GameVariables.score});
这将导入名为 的默认导出。该对象本身是不可重新分配的,但没关系:它的属性可以更改,因此该行按预期工作,总分为 200。GameVariablesGameVariables.score += 100

这是一种有用的技术,用于将相关变量集作为其自己的模块进行管理。例如,您可以将其用于“全局”变量 – 这些变量实际上不在全局范围内(因为它们实际上并不在全局对象上),但提供了一种更好的组织方式来在所有代码中共享一组变量。

可以导出的内容

您可以从脚本文件中导出几乎任何内容。但是,由于导入的任何内容都是只读的(即不可重新分配),因此导出变量并不是很有用,因为它们将始终像使用 声明一样工作。通常模块会导出对象、函数和类,因为这些是最有用的共享内容。const

事件的导入

以前,我们在事件表中使用脚本。在此处使用导入需要另一个步骤。问题在于事件表中的脚本代码实际上是在函数内部运行的,并且不允许在函数内部使用语句。因此,需要有另一种方法来利用事件表中脚本的导入。importexport

不支持从事件表中的脚本导出 – 您必须为此使用脚本文件。但是,使用特殊文件来支持导入,其目的是为事件导入。简而言之,在该文件中导入的所有内容都可用于事件表中的所有脚本。

下载并打开下面的项目,以了解这方面的示例。

.C3P 公司
IMPORTS-FOR-EVENTS.C3P
立即下载 47.58 KB
在此示例中,我们具有以下内容:

  • 在 main.js 中,导出了一个名为的函数,该函数仅返回一个带有消息的字符串。(主脚本仍然可以导出内容并导入到其他地方。GetMessage
  • 还有另一个名为 importsForEvents.js 的脚本文件,该文件将事件的 Purpose 属性设置为 Imports。在此特殊脚本文件中导入的任何内容都可以在事件表中的脚本中使用。此文件导入自 main.js。因此,现在可以在事件表中的所有脚本中使用。GetMessageGetMessage
  • 在事件表 1 中,脚本在布局开始时运行,该脚本会记录到控制台。预览项目,您将看到消息显示在控制台中。GetMessage()
    您还可以使用函数来执行一些工作,而不是调用函数来检索值。例如,如果您有一个包含大量代码的长函数,则将其放在脚本文件中,然后从事件表中的脚本中调用该函数通常更方便。这与上述示例的完成方式完全相同,只是该函数将执行其他操作(并称为其他操作)。GetMessage

使用全局变量是执行此操作的另一种方法,因为在脚本文件和事件表的脚本中都可以访问全局变量。但如前所述,使用全局变量有几个陷阱,因此以这种方式使用模块是可取的。globalThis

另一个有用的方法是将其置于 importsForEvents.js 中:

import * as Main from "./main.js";
然后,事件表中的脚本可以调用从 main.js 导出的任何函数,例如 . 在较大的项目中,您可以对其他脚本文件重复此操作,以便于从事件表中的脚本中调用不同脚本文件中的函数。Main.GetMessage()

事件表中的脚本是 Construct 独有的概念,因此这种使用模块公开函数的方法是 Construct 自己设计的一部分。在事件表中处理脚本时,并不总是有那么多的输入空间(尤其是在操作部分,因为左侧被条件占据),而且通常也有很多事件。因此,一个好的风格是将任何长代码块编写为脚本文件中的函数,并通过导入事件在事件表的脚本中调用这些函数。这使事件表中的 JavaScript 代码保持简短,并可以更轻松地调用脚本文件中的其他函数和导入。

结论

在这一部分中,我们介绍了:

  • 个脚本文件如何成为其自己的模块,默认情况下,该模块保持其内容的私有性

  • 用于在脚本文件之外共享某些内容 export

  • 用于使用另一个脚本文件的导出 import

  • How Construct 只加载主脚本,其他脚本必须导入

  • 使用 和 的其他方式,包括默认导出 importexport

  • 导出如何是只读的,以及如何使用对象属性而不是变量进行导出

  • 使用 Imports for events 脚本从事件表中的脚本调用脚本文件中的函数

    了解更多信息

    我们只介绍了几种可以使用的方法。可以使用更多方法使用它们,包括导入和导出具有默认值和命名项的内容、使用项的子集、重命名项等。查看 MDN Web Docs 上的这些链接以获取更多详细信息。importexport

  • 出口陈述

  • 进口语句(也包括动态导入 )

  • MDN 部分 JavaScript 模块有关于使用模块的更多详细信息

  • 这 Ghost Shooter 代码示例显示了“Ghost shooter”示例的完全 JavaScript 编码版本,使用模块在脚本文件之间共享内容

正文完
 0
评论(没有评论)