Javascript 模块是什么?模块导出与导出之间的区别是什么?

Javascript 模块是什么?模块导出与导出之间的区别是什么?,javascript,node.js,Javascript,Node.js,我已经读了好几个小时关于这个话题的书,只是没有发现任何东西可以帮助我坚持下去 模块只是节点中具有几个属性的对象,一个是引用对象的导出属性 “exports”变量为 var exports = module.exports 它是指向module.exports引用的对象的变量 我正在努力想象模块是什么。我知道这是个物体,但只有一个吗 我知道这不是node实现模块的确切方式,但我将其可视化,如下所示: var module = {} module.exports = {} // now mod

我已经读了好几个小时关于这个话题的书,只是没有发现任何东西可以帮助我坚持下去

模块只是节点中具有几个属性的对象,一个是引用对象的导出属性

“exports”变量为

var exports = module.exports
它是指向module.exports引用的对象的变量

我正在努力想象模块是什么。我知道这是个物体,但只有一个吗

我知道这不是node实现模块的确切方式,但我将其可视化,如下所示:

var module = {}

module.exports = {}

// now module has a property module.exports

var exports = module.exports
evaledFn(module.exports, require, module, filename, dirname);
现在,从我读到的所有内容来看,如果要为module.export='xyz'分配一些内容

它将保存值“xyz”。它是否丢失了原始对象?最重要的是,如果我在同一个文件中为module.exports分配了其他内容,它会被替换为新值吗

EX: 

// file = app.js

module.export = 'hello'
module.export = 'bye'

// file = newApp.js

require(./app);

模块的值是多少?我是重写同一个模块对象还是有多个模块对象?

您正在重写模块-导出是使用
require
拉入单个对象。通常,当使用
require
执行这种模块化JavaScript时,您的导出将是一个构造函数,而不是像示例中的字符串那样的原语。这样,您就可以创建模块中定义的新功能实例。例如:

// module.js
var MyConstructor = function(prop) {
    this.prop = prop;
});

module.exports = MyConstructor;

// app.js
var MyConstructor = require('module');
var instance = new MyConstructor('test');
console.log(instance.prop) // 'test'

Node.js中的模块只是文件。每个文件都是一个模块,每个模块都是一个文件。如果有多个文件,则可以有多个模块(每个文件中有一个)

根据
模块。导出
:将阐明以下主题:

module.exports
对象由模块系统创建。有时 这是不能接受的;许多人希望他们的模块成为 一些班级。为此,请将所需的导出对象指定给
模块。导出
。请注意,将所需对象指定给
导出将
只需重新绑定本地
导出
变量,这可能不是 你想做什么

导出模块中可用的
变量,该变量以 参考
模块。导出
。与任何变量一样,如果您分配一个新的 值,它不再绑定到上一个值。。。作为指导方针, 如果导出和module.exports之间的关系 对您来说似乎很神奇,忽略导出,只使用module.exports


那么,这一切意味着什么?在您的特定示例中,
module.exports
将等于它的最后一个赋值(
'bye'
)。

在继续之前,请务必了解

要从节点的模块加载系统中去掉的关键点是,在它实际运行您所需的代码之前(发生在中),一个新的空
对象导出为
模块的属性。(换句话说,您的可视化是正确的。)

节点,然后使用匿名函数将
require
d文件中的文本:

(function (exports, require, module, __filename, __dirname) {
// here goes what's in your js file
});
…本质上是
eval
s该字符串。
eval
ing该字符串的结果是一个函数(匿名包装器),节点立即调用该函数,传入如下参数:

var module = {}

module.exports = {}

// now module has a property module.exports

var exports = module.exports
evaledFn(module.exports, require, module, filename, dirname);
require
filename
dirname
是对
require
函数(实际上是)的引用,以及包含加载模块路径信息的字符串。(这就是他们所说的“
\uu filename
实际上不是一个全局模块,而是每个模块的本地模块。”)

所以我们可以看到,在一个模块中,确实是
exports===module.exports
。但是为什么模块同时获得
模块
导出

向后兼容性。在节点的早期,模块内部没有
模块
变量。您只需分配到
导出
。但是,这意味着您永远不能将构造函数导出为模块本身

模块将构造函数导出为模块的常见示例:

var express = require('express');
var app = express();
这是因为,在默认情况下覆盖节点提供给您的空对象:

module.exports = function() { ... }
但是,请注意,您不能只写:

exports = function { ... }
这是因为。从技术上讲,JavaScript可能被认为是“纯粹的按值传递”,但实际上参数是“按值引用”传递的

这意味着,当您将对象传递给函数时,它将接收对该对象的引用作为值。您可以通过该引用访问原始对象,但不能更改调用方对该引用的感知。换句话说,没有像您在C/C++/C#中看到的那样的“out”参数

作为一个具体例子:

var obj = { x: 1 };

function A(o) {
    o.x = 2;
}
function B(o) {
    o = { x: 2 };
}
调用
A(obj)
将导致
obj.x==2
,因为我们访问作为
o
传入的原始对象。然而,
B(obj)将什么也不做<代码>对象x==1
。将一个全新的对象分配给
B
的本地
o
只会更改
o
指向的对象。它对调用者的对象obj不做任何操作,该对象保持不变

现在应该很清楚为什么有必要将
模块
对象添加到节点模块的本地范围。为了允许模块完全替换
导出
对象,它必须作为传递给模块匿名函数的对象的属性可用。显然没有人想破坏现有的代码,所以
exports
被作为
module.exports
的参考

因此,当您只是将属性分配给exports对象时,无论是使用
exports
还是
module.exports
;它们是同一个,指向完全相同的对象的引用

只有当您希望将函数导出为必须使用的顶级导出时,才可以使用该函数