node.js中模块
by Srishti Gupta
通过Srishti Gupta
Node.js treats each Javascript file as a separate module.
Node.js将每个Javascript文件视为一个单独的模块。
For instance, if you have a file containing some code and this file is named xyz.js
, then this file is treated as a module in Node, and you can say that you’ve created a module named xyz
.
例如,如果您有一个包含一些代码的文件,并且此文件名为xyz.js
,则此文件在Node中被视为模块 ,可以说您已经创建了一个名为xyz
的模块。
Let’s take an example to understand this better.
让我们举个例子来更好地理解这一点。
You have a file named circle.js
which consists of the logic for calculating the area & the circumference of a circle of a given radius, as given below:
您有一个名为circle.js
的文件,该文件包含用于计算给定半径的圆的面积和周长的逻辑,如下所示:
circle.js
circle.js
You can call circle.js
file a module named circle
.
您可以将circle.js
文件称为一个名为circle
的模块。
You might be wondering why is there a need to have multiple modules? You could have just written all the code in a single module. Well, it is very important to write modular code. By modular, I mean to say that your code should be independent and should be loosely coupled. Imagine that there’s a large application and you have all your code written in just one place, just one file. Too messy, right?
您可能想知道为什么需要多个模块? 您可能已经在一个模块中编写了所有代码。 好吧,编写模块化代码非常重要。 模块化,我的意思是说您的代码应该独立并且应该松散耦合。 想象一下,有一个大型应用程序,而您所有的代码都只写在一个地方,只有一个文件。 太乱了吧?
Before executing the code written inside a module, Node takes the entire code and encloses it within a function wrapper. The syntax of this function wrapper is:
在执行写在模块内部的代码之前,Node将全部代码封装在函数包装器中。 该函数包装的语法为:
The function wrapper for the circle
module will look like the one given below:
circle
模块的功能包装器将如下所示:
You can see that there is a function wrapper at the root level encompassing all the code written inside the circle
module.
您会看到在根级别有一个函数包装器,其中包含在circle
模块中编写的所有代码。
The entire code written inside a module is private to the module, unless explicitly stated (exported) otherwise.
除非另有明确说明(导出),否则模块内部编写的全部代码均为该模块专用。
This is the most significant advantage of having modules in Node.js. Even if you define a global variable in a module using var
, let
or const
keywords, the variables are scoped locally to the module rather than being scoped globally. This happens because each module has a function wrapper of its own and the code written inside one function is local to that function and cannot be accessed outside this function.
这是在Node.js中拥有模块的最大优势。 即使您使用var
, let
或const
关键字在模块中定义了全局变量,变量也只在模块本地作用域,而不是全局范围。 发生这种情况是因为每个模块都有自己的函数包装器,并且在一个函数内部编写的代码是该函数的本地代码,无法在该函数外部访问。
Imagine that there are two modules — A and B. The code written inside the module A is enclosed within the function wrapper corresponding to the module A. Similar thing happens with the code written inside the module B. Because the code pertaining to both the modules is enclosed within different functions, these functions will not be able to access the code of each other. (Remember each function in Javascript has its own local scope?) This is the reason why module A cannot access the code written inside module B and vice-versa.
想象有两个模块-A和B。 写入模块A内的代码被封装在对应于模块A的函数包装器中。 在模块B内编写的代码也会发生类似的情况。 由于与两个模块有关的代码包含在不同的功能内,因此这些功能将无法相互访问代码。 (还记得Javascript中的每个函数都有自己的局部作用域吗?)这就是模块A无法访问模块B内编写的代码的原因,反之亦然。
The five parameters — exports
, require
, module
, __filename
, __dirname
are available inside each module in Node. Though these parameters are global to the code within a module yet they are local to the module (because of the function wrapper as explained above). These parameters provide valuable information related to a module.
五个参数- exports
, require
, module
, __filename
, __dirname
可在每个节点模块内。 尽管这些参数对于模块内的代码是全局的,但它们对于模块而言是局部的(由于上述函数包装器)。 这些参数提供与模块有关的有价值的信息。
Let’s revisit the circle
module, which you looked at earlier. There are three constructs defined in this module — a constant variable PI
, a function named calculateArea
and another function named calculateCircumference
. An important point to keep in mind is that all these constructs are private to the circle
module by default. It means that you cannot use these constructs in any other module unless explicitly specified.
让我们重新访问您之前看过的circle
模块。 在此模块中定义了三个构造-常数变量PI
,一个名为calculateArea
的函数和另一个名为calculateCircumference
函数。 要记住的重要一点是,默认情况下,所有这些构造都是circle
模块专用的。 这意味着除非明确指定,否则您不能在任何其他模块中使用这些构造。
So, the question that arises now is how do you specify something in a module that can be used by some other module? This is when the module
& require
parameters of the function wrapper are helpful. Let’s discuss these two parameters in this article.
因此,现在出现的问题是如何在模块中指定可以被其他模块使用的内容? 这是当函数包装的module
和require
参数有用时。 让我们在本文中讨论这两个参数。
module
(module
)The module
parameter (rather a keyword in a module in Node) refers to the object representing the current module. exports
is a key of the module
object, the corresponding value of which is an object. The default value of module.exports
object is {}
(empty object). You can check this by logging the value of module
keyword inside any module. Let’s check what is the value of module
parameter inside the circle
module.
module
参数(而是Node中模块中的关键字)是表示当前模块的对象。 exports
是module
对象的键, module
对象的对应值是一个对象。 module.exports
对象的默认值为{}
(空对象)。 您可以通过在任何模块内记录module
关键字的值来检查此情况。 让我们检查一下circle
模块中module
参数的值是多少。
circle.js
circle.js
Notice that there is a console.log(module);
statement at the end of the code in the file given above. When you see the output, it will log the module
object, which has a key named exports
and the value corresponding to this key is {}
(an empty object).
注意,有一个console.log(module);
上面给出的文件中代码末尾的语句。 当您看到输出时,它将记录module
对象,该对象具有名为exports
的键,并且与该键对应的值为{}
(空对象)。
Now, what does the module.exports
object do? Well, it is used for defining stuff that can be exported by a module. Whatever is exported from a module can, in turn, be made available to other modules. Exporting something is quite easy. You just need to add it to the module.exports
object. There are three ways to add something to the module.exports
object to be exported. Let’s discuss these methods one by one.
现在, module.exports
对象做什么? 好吧,它用于定义可以由模块导出的内容。 反过来,从模块导出的所有内容都可以提供给其他模块。 导出内容非常容易。 您只需要将其添加到module.exports
对象即可。 有三种方法可以将某些内容添加到要导出的module.exports
对象中。 让我们一一讨论这些方法。
Method 1:(Defining constructs and then using multiple module.exports
statements to add properties)
方法1 :( 定义构造,然后使用多个module.exports
语句添加属性)
In the first method, you define the constructs first and then use multiple module.exports statements where each statement is used to export something from a module. Let’s look at this method in action and see how you can export the two functions defined in the circle
module.
在第一种方法中,首先定义结构,然后使用多个module.exports语句,其中每个语句用于从模块中导出内容。 让我们看一下实际使用的此方法,看看如何导出在circle
模块中定义的两个函数。
circle.js
circle.js
As I told you earlier, module
is an object having the key named exports
and this key (module.exports
), in turn, consists of another object. Now, if you notice the code given above, all you are doing is adding new properties (key-value pairs) to the module.exports
object.
正如我告诉你之前, module
是具有名为key对象exports
,并且这个键( module.exports
),反过来,由另一个对象。 现在,如果您注意到上面给出的代码,您正在做的就是向module.exports
对象添加新属性(键值对)。
The first property has the key calculateArea
(defined on line 19) and the value written on the right side of the assignment operator is the function defined with the name calculateArea
(on line 9).
第一个属性具有键calculateArea
(在第19行定义) 并且写在赋值运算符右侧的值是使用名称calculateArea
定义的函数(第9行)。
The second property (defined on line 20) has the key calculateCircumference
and the value is the function defined with the name calculateCircumference
(on line 16).
第二个属性(在第20行定义)的键为calculateCircumference
该值是使用名称calculateCircumference
定义的函数(第16行)。
Thus, you have assigned two properties (key-value pairs) to the module.exports
object.
因此,您已为module.exports
对象分配了两个属性(键-值对)。
Also, let’s not forget that you have used the dot notation here. You can alternatively use the bracket notation for assigning the properties to the module.exports
object and add the functions — calculateArea
and calculateCircumference
by specifying the keys following the bracket notation. Thus, you can write the following two lines to add properties to the module.exports
object using bracket notation while replacing the last two lines (using dot notation) in the code given above:
另外,请不要忘记您在此处使用了点符号。 可以替代地使用括号标记用于分配属性到module.exports
对象并添加功能- calculateArea
和calculateCircumference
通过指定以下的括号符号的键。 因此,您可以编写以下两行以使用括号表示法将属性添加到module.exports
对象,同时替换上面给出的代码中的最后两行(使用点表示法):
// exporting stuff by adding to module.exports object using the bracket notation
module.exports['calculateArea'] = calculateArea;module.exports['calculateCircumference'] = calculateCircumference;
Let’s now try to log the value of the module.exports
object after adding the properties. Notice that the following statement is added at the end of the code in the file given below:
现在,让我们尝试在添加属性后记录module.exports
对象的值。 请注意,以下语句添加在下面给出的文件中代码的末尾:
// logging the contents of module.exports object after adding properties to it
console.log(module.exports);
circle.js
circle.js
Let’s check the output of this code and see if everything is working fine. To do this, save your code and run the following command in your Terminal:
让我们检查一下此代码的输出,看看一切是否正常。 为此,请保存代码并在终端中运行以下命令:
node circle
Output:
输出:
{ calculateArea: [Function: calculateArea], calculateCircumference: [Function: calculateCircumference] }
The constructs — calculateArea
and calculateCircumference
, added to the module.exports
, object are logged. Thus, you successfully added the two properties in the module.exports
object so that the functions — calculateArea
and calculateCircumference
can be exported from the circle
module to some other module.
该构造- calculateArea
和calculateCircumference
,加入到module.exports
,对象被记录。 因此,你成功添加在两个属性module.exports
对象,以使功能- calculateArea
和calculateCircumference
可以从导出circle
模块到其他模块。
In this method, you first defined all the constructs and then used multiple module.exports statements where each statement is used to add a property to the module.exports
object.
在此方法中,您首先定义了所有构造,然后使用了多个module.exports语句,其中每个语句用于向module.exports
对象添加属性。
Method 2:(Defining constructs and then using a single module.exports
statement to add properties)
方法2 :( 定义构造,然后使用单个module.exports
语句添加属性)
Another way is to define all the constructs first (as you did in the earlier method) but use a single module.exports
statement to export them all. This method is similar to the syntax of object literal notation where you add all the properties to an object at once.
另一种方法是先定义所有构造(就像您在前面的方法中所做的那样),但是使用单个module.exports
语句将它们全部导出。 此方法类似于对象文字符号的语法,在该语法中,您一次将所有属性添加到对象。
Here, you used the object literal notation and added both the functions — calculateArea
and calculateCircumference
(all at once) to the module.exports
object by writing a single module.exports statement.
在这里,你使用的对象文字符号,并加入两种功能- calculateArea
和calculateCircumference
(一次性)的module.exports
对象通过写一个module.exports声明。
If you check the output of this code, you will get the same result as you got earlier when using method 1.
如果检查此代码的输出,则将获得与使用方法1时获得的结果相同的结果。
Method 3:(Adding properties to the module.exports
object while defining constructs)
方法3:( 在定义构造时将属性添加到module.exports
对象)
In this method, you can add the constructs to the module.exports
object while defining them. Let’s see how this method can be adopted in our circle
module.
在此方法中,可以在定义结构时将其添加到module.exports
对象。 让我们看看如何在我们的circle
模块中采用这种方法。
In the code given above, you can see that the functions in the module are added to the module.exports
object when they are being defined. Let’s look at how this is working. You are adding a key calculateArea
to the module.exports
object and the value corresponding to this key is the function definition.
在上面给出的代码中,您可以看到模块中的功能在定义时已添加到module.exports
对象中。 让我们看看它是如何工作的。 您正在将一个关键calculateArea
添加到module.exports
对象,并且与该关键相对应的值是函数定义。
Note that the function no longer has any name and is an anonymous function which is just treated as a value to a key of an object. Thus, this function cannot be referenced to in the circle
module and you cannot invoke this function inside this module by writing the following statement:
请注意,该函数不再具有任何名称,而是一个匿名函数,仅被视为对象键的值。 因此,无法在circle
模块中引用此函数,并且无法通过编写以下语句在此模块内部调用此函数:
calculateArea(8);
If you try to execute the above statement, you will get a ReferenceError
stating calculateArea is not defined
.
如果您尝试执行上述语句,则会得到一个ReferenceError
指出calculateArea is not defined
。
Now that you have learned how you can specify what needs to be exported from a module, how do you think the other module will be able to use the exported stuff? You need to import the module to some other module so as to be able to use the exported stuff from the former in the latter. This is when we need to discuss another parameter named require
.
既然您已经了解了如何指定需要从一个模块导出的内容,那么您如何看待另一个模块将能够使用导出的内容? 您需要将模块导入到其他模块,以便能够使用后者中的前者导出的内容。 这是我们需要讨论另一个名为require
参数的时候。
require
keyword refers to a function which is used to import all the constructs exported using the module.exports
object from another module. If you have a module x in which you are exporting some constructs using the module.exports
object and you want to import these exported constructs in module y, you then need to require the module x in the module y using the require
function. The value returned by the require
function in module y is equal to the module.exports
object in the module x.
require
关键字是指用于导入从另一个模块使用module.exports
对象导出的所有构造的函数。 如果您有一个要使用module.exports
对象在其中导出某些构造的模块x ,并且要在模块y中导入这些导出的构造,则需要使用require
函数在模块y中要求该模块x 。 模块y中 require
函数返回的值等于模块x中的module.exports
对象。
Let’s understand this using the example which we discussed earlier. You already have the circle
module from which you are exporting the functions calculateArea
and calculateCircumference
. Now, let’s see how you can use the require
function to import the exported stuff in another module.
让我们使用我们前面讨论的示例来理解这一点。 您已经有一个circle
模块,可以从中导出函数calculateArea
和calculateCircumference
。 现在,让我们看看如何使用require
函数将导出的内容导入另一个模块。
Let’s first create a new file wherein you will be using the exported code from the circle
module. Let’s name this file app.js
and you can call it the app
module.
首先创建一个新文件,其中将使用来自circle
模块的导出代码。 我们将此文件命名为app.js
,您可以将其称为app
模块。
The objective is to import into the app
module all the code exported from the circle
module. So, how can you include your code written in one module inside another module?
目的是将从circle
模块导出的所有代码导入到app
模块中。 那么,如何将编写在一个模块中的代码包含在另一个模块中呢?
Consider the syntax of the require
function given below:
考虑下面给出的require
函数的语法:
const variableToHoldExportedStuff = require('idOrPathOfModule');
The require
function takes in an argument which can be an ID or a path. The ID refers to the id (or name) of the module needed. You should provide ID as an argument when you are using the third-party modules or core modules provided by the Node Package Manager. On the other hand, when you have custom modules defined by you, you should provide the path of the module as the argument. You can read more about the require function from this link.
require
函数接受一个参数,该参数可以是ID或路径。 ID是指所需模块的ID(或名称)。 使用Node Package Manager提供的第三方模块或核心模块时,应提供ID作为参数。 另一方面,当您定义了自定义模块时,应提供模块的路径作为参数。 你可以关于从需要功能此链接。
Because you’ve already defined a custom module named circle
, you’ll provide the path as an argument to the require
function.
因为您已经定义了一个名为circle
的自定义模块,所以您将提供路径作为require
函数的参数。
app.js
app.js
If you notice clearly, the dot at the start of the path means that it is a relative path and that the modules app
and circle
are stored at the same path.
如果您清楚地注意到,路径开头的点表示它是相对路径,并且模块app
和circle
存储在同一路径中。
Let’s log on to the console the circle
variable, which contains the result returned by the require
function. Let’s see what is contained inside this variable.
让我们将circle
变量登录到控制台,该变量包含require
函数返回的结果。 让我们看看该变量中包含什么。
app.js
app.js
Check the output by saving all your code and running the following command in your Terminal (latter isn’t required if you have nodemon
package installed):
通过保存所有代码并在终端中运行以下命令来检查输出(如果已安装nodemon
软件包,则nodemon
):
node app
Output:
输出:
{ calculateArea: [Function: calculateArea],calculateCircumference: [Function: calculateCircumference] }
As you can see, the require
function returns an object, the keys of which are the names of the variables/functions that have been exported from the required module (circle
). In short, the require
function returns the module.exports
object.
如您所见, require
函数返回一个对象,其键是已从所需模块( circle
)导出的变量/函数的名称。 简而言之, require
函数返回module.exports
对象。
Let’s now access the functions imported from the circle
module.
现在,让我们访问从circle
模块导入的功能。
app.js
app.js
Output:
输出:
Area = 200.96, Circumference = 50.24
What do you think will happen if I try to access the variable named PI
defined in the circle
module inside the app
module?
如果我尝试访问在app
模块内部的circle
模块中定义的名为PI
的变量,您会怎么办?
app.js
app.js
Output:
输出:
Area = 200.96, Circumference = 50.24pi = undefined
Can you figure out why pi
is undefined
? Well, this is because the variable PI
is not exported from the circle
module. Remember the point where I told you that you cannot access the code written inside a module in another module for all the code written inside a module is private to it unless exported? Here, you are trying to access something which has not been exported from the circle
module and is private to it.
您能找出为什么pi
是undefined
吗? 好吧,这是因为变量PI
没有从circle
模块导出。 还记得我曾告诉您的问题,您不能访问另一个模块中写在模块中的代码,因为除非导出,否则写在模块中的所有代码都是私有的? 在这里,您尝试访问的内容尚未从circle
模块导出,并且是私有的。
So, you may be wondering why you didn’t get a ReferenceError
. This is because you are trying to access a key named PI
inside the module.exports
object returned by the require
function. You also know that the key named PI
does not exist in the module.exports
object.
因此,您可能想知道为什么没有得到ReferenceError
。 这是因为您试图访问require
函数返回的module.exports
对象内名为PI
的键。 您还知道,名为PI
的键在module.exports
对象中不存在。
Note that when you try to access a non-existent key in an object, you get the result as undefined
. This is the reason why you get PI
as undefined
instead of getting a ReferenceError
.
请注意,当您尝试访问对象中不存在的键时,结果为undefined
。 这就是为什么您将PI
设为undefined
而不是得到ReferenceError
。
Now, let’s export the variable PI
from the circle
module and see if the answer changes.
现在,让我们从circle
模块中导出变量PI
,看看答案是否改变。
circle.js
circle.js
Notice that here, you are not using the name of the variable PI
as the key of the property added to the module.exports
object. You are, instead, using another name, which is lifeOfPi
.
请注意,此处没有使用变量PI
的名称作为添加到module.exports
对象的属性的键。 相反,您使用的是另一个名称,即lifeOfPi
。
This is an interesting thing to note. When you are exporting some coding construct, you can give any name to the key when adding a property added to the module.exports
object. It is not mandatory to use the same name as the name you used while defining the construct. This is because you can use any valid identifier as the key in a Javascript object. Thus, on the left side of the assignment operator, you can use any valid identifier, but on the right side of the assignment operator, you need to provide a value which is defined as a construct in the scope of the current module (as you’ve defined the variables and functions in the ‘circle’ module).
这是一个有趣的事情。 导出某些编码构造时,在添加添加到module.exports
对象的属性时,可以为键指定任何名称。 使用与定义结构时使用的名称相同的名称不是强制性的。 这是因为您可以将任何有效的标识符用作Javascript对象中的键。 因此,在赋值运算符的左侧,您可以使用任何有效的标识符,但是在赋值运算符的右侧,您需要提供一个值,该值被定义为当前模块范围内的构造(已在“圆圈”模块中定义了变量和函数)。
An important point to be noted is that while importing something from another module in the current module, you need to use the same key which you used while exporting it.
需要注意的重要一点是,从当前模块中的另一个模块导入内容时,需要使用与导出时相同的密钥。
app.js
app.js
Because you used the key lifeOfPi
, you need to use the same key to access the variable PI
defined in the circle
module, as is done in the code given above.
因为您使用的是lifeOfPi
键,所以您需要使用相同的键来访问circle
模块中定义的变量PI
,就像上面给出的代码一样。
Output:
输出:
Area = 200.96, Circumference = 50.24pi = 3.14
What do you think will happen if you use the name of the variable instead of using the key which was used while exporting? In short, let’s try to access PI
(name of the variable) instead of lifeOfPi
(key used while exporting PI
).
如果您使用变量的名称而不是使用导出时使用的键,您会怎么办? 简而言之,让我们尝试访问PI
(变量的名称),而不是lifeOfPi
(导出PI
使用的键)。
app.js
app.js
Output:
输出:
Area = 200.96, Circumference = 50.24pi = undefined
This happens because the module.exports
object does not know the variable PI
anymore. It just knows about the keys added to it. Because the key used for exporting the variable PI
is lifeOfPi
, the latter can only be used to access the former.
发生这种情况是因为module.exports
对象不再知道变量PI
。 它只知道添加到其中的键。 因为用于导出变量PI
的密钥是lifeOfPi
,所以后者只能用于访问前者。
Each file in Node.js is referred to as a module.
Node.js中的每个文件都称为一个模块 。
(function(exports, require, module, __filename, __dirname) { // entire module code lives in here});
The function wrapper ensures that all the code written inside a module is private to it unless explicitly stated otherwise (exported). The parameters exports
, require
, module
, __filename
, and __dirname
act as the variables global to the entire code in a module. Since each module has a function wrapper of its own, the code written inside one function wrapper becomes local to that function wrapper (read module) and is not accessible inside another function wrapper (read module).
函数包装器确保模块内编写的所有代码均为该模块专用,除非另有明确说明(导出)。 参数exports
, require
, module
, __filename
和__dirname
全球的模块整个代码中的变量行为。 由于每个模块都有自己的功能包装器,因此在一个功能包装器中编写的代码将成为该功能包装器(读取模块)的本地代码,而在另一个功能包装器(读取模块)中不可访问。
module
keyword refers to the object representing the current module. The module
object has a key named exports
. module.exports
is another object which is used for defining what can be exported by a module and can be made available to other modules. In short, if a module wants to export something, it should be added to the module.exports
object.
module
关键字是指代表当前模块的对象。 module
对象有一个名为exports
的键。 module.exports
是另一个对象,用于定义模块可以导出的内容以及可用于其他模块的内容。 简而言之,如果模块要导出某些内容,则应将其添加到module.exports
对象。
The default value of module.exports
object is {}
.
module.exports
对象的默认值为{}
。
There are three methods in which you can export something from a module, or add something to the module.exports
object:
您可以通过三种方法从模块中导出某些内容,或将某些内容添加到module.exports
对象中:
1. Define all the constructs first and then use multiple
1.首先定义所有构造,然后使用多个
module.exports
statements where each statement is used to export a construct.
module.exports
语句,其中每个语句都用于导出构造。
2. Define all the constructs first and then use a single
2.首先定义所有构造,然后使用一个
module.exports
statement to exports all constructs at once following object literal notation.
module.exports
语句按照对象文字符号一次导出所有构造。
3. Add constructs to the
3.将构造添加到
module.exports
object while defining them.
定义它们时, module.exports
对象。
require
keyword refers to a function which is used to import all the variables and functions exported using the module.exports
object from another module. In short, if a file wants to import something it has to declare it using the following syntax:
require
关键字指的是一个函数,该函数用于导入从另一个模块使用module.exports
对象导出的所有变量和函数。 简而言之,如果文件要导入某些内容,则必须使用以下语法对其进行声明:
require('idOrPathOfModule');
While exporting something from a module, you can use any valid identifier. It is not mandatory that you need to give the exact name of the variable/function as the key of the property added to module.exports
object. Just make sure that you use the same key for accessing something which you used while exporting it.
从模块导出内容时,可以使用任何有效的标识符。 您不必提供确切的变量/函数名称作为添加到module.exports
对象的属性的键。 只需确保使用相同的密钥即可访问导出时使用的密钥。
翻译自: https://www.freecodecamp.org/news/require-module-in-node-js-everything-about-module-require-ccccd3ad383/
node.js中模块