# module

## Description

为了编写可维护的代码，我们把很多函数分组，分别放到不同的文件里，这样，每个文件包含的代码就相对较少，很多编程语言都采用这种组织代码的方式。在 Node 环境中，一个 js 文件就称之为一个模块（module）

使用模块可以避免函数名和变量名冲突，相同名字的函数和变量完全可以分别存在不同的模块中。

## Code

### hello 模块

```javascript
'use strict'

var s = 'Hello'

function greet(name) {
  console.log(s + ', ' + name + '!')
}

module.exports = greet

```

函数 `greet()` 是我们在 `hello` 模块中定义的，你可能注意到最后一行是一个奇怪的赋值语句，它的意思是，把函数 `greet` 作为模块的输出暴露出去，这样其他模块就可以使用 `greet` 函数了

## 调用模块

```javascript
'use strict'

// 引入 hello 模块
var greet = require('./hello')

var s = 'Michael'

greet(s) // Hello, Michael

```

> require 引入的就是 hello 模块输出的 greet

## CommonJS 规范

`NodeJS` 的模块加载机制被称为 `CommonJS` 规范。在这个规范下，每个 `.js` 文件都是一个模块，它们内部各自使用的变量名和函数名都互不冲突，例如，`hello.js` 和 `main.js` 都申明了全局变量 `var s = 'xxx'`，但互不影响

### 用法

在模块中对外输出变量，用：

```javascript
module.exports = variable
```

输出的变量可以是任意对象、函数、数组等

要引入其他模块输出的对象，用：

```javascript
var foo = require('other_module')
```

引入的对象具体是什么，取决于引入模块输出的对象

## Node 模块原理

### require

Node.js 加载模块时，会将代码包装：

```javascript
(function () {
  // 读取的 hello.js 代码
  var s = 'Hello'
  var name = 'world'
  
  console.log(s + ' ' + name + '!')
  // hello.js 代码结束
})()
```

这样，模块中的变量都会变成匿名函数内部的局部变量，即使 NodeJS 继续加载其他模块，模块之间的变量也不会互相干扰

### exports

`NodeJS` 输出模块时，会先准备一个对象 `module`

```javascript
// 准备 module 对象
var module = {
  id: 'hello',
  exports: {}
}

var load = function (module) {
  // 读取的 hello.js 代码
  function greet(name) {
    console.log('Hello, ' + name + '!')
  }
  
  module.exports = greet
  // hello.js 代码结束
  
  return module.exports
}

var exported = load(module)
// 保存 module
save(module, exported)
```

通过把参数 `module` 传递给 `load` 函数，`hello.js` 就顺利地把一个变量传递给了 `Node` 执行环境，`Node` 会把 `module` 变量保存到某个地方。

由于 `Node` 保存了所有导入的 `module`，当我们用 `require()` 获取 `module` 时，`Node` 找到对应的 `module`，把这个 `module` 的 `exports` 变量返回，这样，另一个模块就顺利拿到了模块的输出：

```javascript
var greet = require('./hello')
```

以上是 `Node` 实现 `javascript` 模块的一个简单的原理
