module.exports 和 exports 区别

1,305 阅读2分钟

概述

Node 应用是有模块组成的,一个文件就是一个模块,有单独的作用域,文件中声明的变量、方法和类都是私有的,如果需要在外部能访问,则需要使用 module.exports 或则 exports 暴露出去

module 对象

Node 内部提供一个 Module 构建函数。模块是Module的实例。

function Module(id, parent) {
  this.id = id;
  this.exports = {};
  this.parent = parent;
  // ...

每个模块内部都有一个 module 对象,module 代表模块本身,module 的 exports 属性(即 module.exports)是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。

module.id 模块的识别符,通常是带有绝对路径的模块文件名。
module.filename 模块的文件名,带有绝对路径。
module.loaded 返回一个布尔值,表示模块是否已经完成加载。
module.parent 返回一个对象,表示调用该模块的模块。
module.children 返回一个数组,表示该模块要用到的其他模块。
module.exports 表示模块对外输出的值。

require 方法用于加载模块,即加载的是 module.exports 对象

示例

可以结合下面的例子进行理解

// example.js
let baseNum = 1;
let addOne = function (num) {
  return baseNum + num;
}

module.exports.baseNum = baseNum;
module.exports.addOne = addOne;
console.log('example', module)
// main.js
let example = require('./example.js')
console.log(example)
console.log(example.baseNum);
console.log(example.addOne(5));

example.js 和 main.js 是两个单独的文件,可以看做是两个模块,正常情况下在 main.js 文件中是访问不到 example.js 文件里的 baseNum 和 addOne 的,但是 example 文件里使用了 module.exports, 所以 main 里可以访问到。

example 里的 module 可以看做是 example.js 这个模块本身的实例, 执行 node example.js 输出 module 结果:

// console.log('example', module)
example Module {
  id: '.',
  exports: { baseNum: 1, addOne: [Function: addOne] },
  parent: null,
  filename: '/home/user/Desktop/nodetest/example.js',
  loaded: false,
  children: [],
  paths:
   [ '/home/user/Desktop/nodetest/node_modules',
     '/home/user/Desktop/node_modules',
     '/home/user/node_modules',
     '/home/node_modules',
     '/node_modules' ] }

main 文件中引入了 example 文件,相当于引入了 main 模块的 module.exports,执行 node main.js 输出结果如下:

// example.js  console 输出结果
Module {
  id: '/home/user/Desktop/nodetest/example.js',
  exports: { baseNum: 1, addOne: [Function: addOne] },
  parent:
   Module {
     id: '.',
     exports: {},
     parent: null,
     filename: '/home/user/Desktop/nodetest/main.js',
     loaded: false,
     children: [ [Circular] ],
     paths:
      [ '/home/user/Desktop/nodetest/node_modules',
        '/home/user/Desktop/node_modules',
        '/home/user/node_modules',
        '/home/node_modules',
        '/node_modules' ] },
  filename: '/home/user/Desktop/nodetest/example.js',
  loaded: false,
  children: [],
  paths:
   [ '/home/user/Desktop/nodetest/node_modules',
     '/home/user/Desktop/node_modules',
     '/home/user/node_modules',
     '/home/node_modules',
     '/node_modules' ] }
// main.js console.log(example) 输出结果
{ baseNum: 1, addOne: [Function: addOne] }
1
6

exports 变量

为了方便,Node为每个模块提供一个 exports 变量,指向 module.exports。这等同在每个模块头部,有一行这样的命令: var exports = module.exports;,所以上面的example.js 可以改成:

// example.js
let baseNum = 1;
let addOne = function (num) {
  return baseNum + num;
}

exports.baseNum = baseNum;
exports.addOne = addOne;
console.log('example', module)

需要注意的是下面两种方式是错误的

exports = addOne; // exports 被重新赋值了,不在指向module.exports
exports.addOne = addOne;
module.exports = 'hello'; // exports 指向 module.exports, module.exports 指向了一个字符串,所以addOne不能被外部访问了