在客户端,JavaScript运行在浏览器中。当我们定义一个变量,它的作用域是全局的。比如我们在文件app.js中定义一个sayHello函数,它的作用域是全局,可以通过window对象来访问。
/* app.js文件 */
let sayHello = function() {
console.log('hello');
}
window.sayHello();
但这样会产生一个问题,那就是在真实的编程中,我们经常会将不同的代码放到不同的文件中去。如果这时在2个文件中都有定义了同名的sayHello函数,被自动添加到了window对象当中,而最新的定义会覆盖先前的定义。造成全局作用域的污染。所以在建立可信和可维护的应用时应避免定义全局变量和函数。这时候就要用到模块化,创建小型的拼装块或是模块来存放变量和函数。

node的核心概念就是模块,每个node中的文件都被看做模块,每个模块中定义的变量和函数作用域仅在模块内。以面向对象的观点我们称之为私有成员,在容器外,也就是模块外是不可见的。如果你要在模块外使用一个定义在模块中的变量或函数,外卖需要明确的导出它为公开成员。
每个node工程至少要包含一个文件或者说一个模块,这里的app.js就是这个项目的主模块。
删除文件中的代码,然后把module对象打印出来看下。

JSON对象,它包含了键值对,比如id,每个模块都有独一无二的id。exports表示可以使用它创建你的模块,添加到这个对象的属性可以在外部访问。parent表示最先引用该模块的模块。filename也就是这个模块的物理位置。loader表示这个模块是否被加载。children表示被该模块引用的模块对象。paths表示模块的搜索路径。
所以在node中,每个文件都是模块,模块中定义的成员作用域只在模块中,它们在模块外是不可见的。那我们如何创建模块并加载模块呢?
首先我们创建一个logger.js的新文件,假设我们为记录信息创建一个模块,在很多的地方会复用到这个模块。有可能也会在其它应用里用到这个模块。

URL,可以通过给它发送HTTP请求来记录日志。
/* logger.js文件 */
let url = 'http://mylogger.io/log';
function log(message) {
//发送http请求
console.log(message);
}
//在exports对象中添加log方法,将这里的log函数赋值给它
module.exports.log = log;
为了保持模块的简单使用,原则就是公开最小限度的成员。在logger模块中,这个url是实现细节,其它的模块不需要了解它,它们只需要调用log函数就可以了。让log变为公开,而保持url私有。好的,现在logger模块已经做好了,接下来就是要到app.js中使用它。
将app.js中的代码都删除,加载模块需要用到require函数,这是node才有的函数,浏览器是没有的。这个函数需要一个参数,也就是我们想加载的模块名称。
/* app.js文件 */
//引入模块
let logger = require('./logger');
console.log(logger);

log,这样就可以在app.js中调用这个函数了。
/* app.js文件 */
//引入模块,可不写后缀名,node会自动补全
let logger = require('./logger');
logger.log('hello node');

node中模块的工作方式,定义一个模块,导出一个或多个成员。为了使用模块,我们使用require函数进行导入。最佳实践的是将导入的模块保存在常量中。使用const。避免意外的将logger重新赋值。
const logger = require('./logger');
有时候我们不想导出一个对象,只是想导出一个简单的函数,如上所示一样。导出对象是在有多个属性或者方法时才用得到,所以改造下logger.js和app.js的代码。
/* logger.js文件 */
let url = 'http://mylogger.io/log';
function log(message) {
//发送http请求
console.log(message);
}
module.exports = log;
/* app.js文件 */
let log = require('./logger');
log('hello node');

node包含了很多有用和常用的模块。这个在实际编程中都会用到,具体的可以从nodejs官网查看学习。