Node.js学习笔记(三) 模块系统

简介: Node.js学习笔记(三) 模块系统

正文


1、模块的基本介绍


(1)模块的介绍


在 Node 中,每个文件都可以看作是一个模块,每个模块都有自己的作用域,存在一个属于自己的命名空间


但是模块之间绝对不是孤立存在的,不同模块之间需要相互协作才能发挥作用,因此模块还要有对外暴露的接口


(2)模块的分类


在 Node 中,模块分为三类,分别是核心模块(也称内置模块)、第三方模块和自定义模块


  • 核心模块 是 Node 的内置模块,被编译成二进制文件,放在 lib 文件夹下
  • 第三方模块 一般通过 npm install 下载,放在 node_modules 文件夹下
  • 自定义模块 由用户自己编写,可以放在项目中的任何位置


2、模块的导入


(1)加载的顺序

我们可以使用 require 语句导入模块,其接收一个字符串作为 模块标识,模块加载的顺序如下:

若模块标识是绝对路径或相对路径(以 /./../ 开头),将会被识别为 自定义模块


  • 如果没有省略文件后缀,例如 require('./entrance.js')
  • 将会直接在指定目录下查找指定文件,如果没有找到,抛出异常 Cannot find module
  • 如果省略文件后缀,例如 require('./entrance')


首先在指定目录下查找,顺序如下:entrance.js -> entrance.json -> entrance.node如果没有找到,将 entrance 当作一个目录,在该目录下继续查找

顺序如下:package.json(main 字段) -> index.js -> index.json -> index.node

如果没有找到,抛出异常 Cannot find module


如果模块标识不是绝对路径或相对路径,将会被识别为 核心模块第三方模块


如果是核心模块,例如 require('http')


  • 直接查找核心模块,如果没有找到,抛出异常 Cannot find module


若是第三方模块,例如 require('express')


  • 首先在当前目录中的 node_modules 目录下查找
  • 如果没有找到,那么在上一级目录中的 node_modules 目录下继续查找,直至文件系统的根目录
  • 如果没有找到,抛出异常 Cannot find module


可以看到,加载模块的整个过程是十分复杂的,幸好 Node 为我们提供 缓存机制,可以提高加载速度

  • 每个模块都会在第一次加载时被缓存,之后如果需要再次加载,将会直接读取缓存
  • 每次加载模块首先从缓存中查找,如果在缓存中没有找到,才会按照上面的加载顺序执行


(2)加载的实质

使用 require 语句加载模块,实际上是同时完成两件事情

一是执行加载模块中的代码,二是返回加载模块中的 module.exports,先看下面的例子:

// a.js
module.exports = function(x , y) { return x + y }
console.log('a')
var subtract = require('./b.js')
var result = subtract(5, 2)
console.log(result) // 3


// b.js
module.exports = function(x , y) { return x - y }
console.log('b')
var add = require('./a.js')
var result = add(5, 2)
console.log(result) // 7

输入 node a.js 命令,运行 a.js 文件,结果如下(注意一下打印顺序):

// a
// b
// 7
// 3


使用 require 语句,有两个地方需要注意:

  • 当遇到 require 语句时,会先执行 require 语句中指定的文件,而挂起当前文件的执行
  • 无论模块被 require 多少次,只会执行一次


3、模块的导出


(1)module.exports

每个模块都有一个 module 对象,每个 module 对象都有一个 exports 属性,也是一个对象

当我们需要导出数据时,可以将数据挂载到 module.exports 下即可


  • 导出多个成员
// 使用 require 得到的是 object
// 方法一
module.exports.number = 123
module.exports.string = 'hello'
module.exports.object = {}
module.exports.array = []
// 方法二
module.exports = {
    number: 123,
    string: 'hello',
    object: {},
    array: []
}


  • 导出单个成员
module.exports = function (x, y) { // 使用 require 得到的是 function
  return x + y
}


(2)exports

为了使用方便,Node 提供 exports 作为 module.exports 的引用

当我们需要导出多个成员时,可以使用以下的写法:

// 方法一
exports.number = 123
exports.string = 'hello'
exports.object = {}
exports.array = []
// 方法二
exports = {
    number: 123,
    string: 'hello',
    object: {},
    array: []
}


但是不能使用 exports 导出单个成员,因为模块最终导出的是 module.exports

而当我们给 exports 重新赋值时,会使得 exports 指向 module.exports 的引用失效


4、模块的本质


对于一个模块而言,有两个关键的地方,一是有自己的作用域,二是有对外暴露的接口

不知道大家有没有想过这样一个问题,模块是怎么实现上面两个特性的呢?其实通过一个 立即执行函数 就可以了


实际上 Node 在编译的时候,会在我们写的代码外包上一层立即执行函数,并传入一些必须的参数


  • exports:用于导出模块数据,module.exports 的一个引用
  • require:用于导入其它模块
  • module:包含当前模块的基本信息
  • __filename:当前模块的绝对路径
  • __dirname:当前模块所在目录的绝对路径
(function (exports, require, module, __filename, __dirname) {
    /* 我们写的代码 */
})()

我们上面使用的 requireexports 都是在编译时传入的,我们可以把传入的参数打印出来看一下

// 打印
console.log(__filename)
console.log(__dirname)
console.log(exports)
console.log(module)
console.log(require)
/*
 * 执行结果:
 * C:\Users\username\Desktop\index.js
 * C:\Users\username\Desktop
 * {}
 * Module {
 *   id: '.',
 *   exports: {},
 *   parent: null,
 *   filename: 'C:\\Users\\username\\Desktop\\index.js',
 *   loaded: false,
 *   children: [],
 *   paths:
 *   [ 'C:\\Users\\username\\Desktop\\node_modules',
 *     'C:\\Users\\username\\node_modules',
 *     'C:\\Users\\node_modules',
 *     'C:\\node_modules'
 *   ]
 * }
 * { [Function: require]
 *   resolve: { [Function: resolve] paths: [Function: paths] },
 *   main: Module {
 *     id: '.',
 *     exports: {},
 *     parent: null,
 *     filename: 'C:\\Users\\username\\Desktop\\index.js',
 *     loaded: false,
 *     children: [],
 *     paths:
 *     [ 'C:\\Users\\username\\Desktop\\node_modules',
 *       'C:\\Users\\username\\node_modules',
 *       'C:\\Users\\node_modules',
 *       'C:\\node_modules'
 *     ]
 *   },
 *   extensions: [Object: null prototype] {
 *     '.js': [Function],
 *     '.json': [Function],
 *     '.node': [Function]
 *   },
 *   cache: [Object: null prototype] {
 *     'C:\\Users\\username\\Desktop\\index.js': Module {
 *       id: '.',
 *       exports: {},
 *       parent: null,
 *       filename: 'C:\\Users\\username\\Desktop\\index.js',
 *       loaded: false,
 *       children: [],
 *       paths: [Array]
 *     }
 *   }
 * }



文章知识点与官方知识档案匹配,可进一步学习相关知识

目录
相关文章
|
4月前
|
JavaScript 前端开发
在Node.js中,如何合理使用模块来避免全局变量的问题?
在Node.js中,如何合理使用模块来避免全局变量的问题?
181 71
|
3月前
|
JavaScript Linux 内存技术
Debian 11系统下Node.js版本更新方法详解
本指南详细介绍在Linux系统中安装和管理Node.js的步骤。首先检查现有环境,包括查看当前版本和清除旧版本;接着通过NodeSource仓库安装最新版Node.js并验证安装结果。推荐使用nvm(Node Version Manager)进行多版本管理,便于切换和设置默认版本。同时,提供常见问题解决方法,如权限错误处理和全局模块迁移方案,以及版本回滚操作,确保用户能够灵活应对不同需求。
231 0
|
3月前
|
JavaScript Linux 内存技术
Debian 11系统下Node.js版本更新方法
Debian 11更新Node.js主要就是这三种方式,无论你是初涉其中的新手还是找寻挑战的专家,总有一种方式能满足你的需求。现在,你已经是这个
269 80
|
4月前
|
JavaScript 前端开发 开发者
Node学习笔记:HTTP模块
总的来说,Node.js的HTTP模块是一个强大的工具,可以帮助你处理HTTP协议的各种需求。无论你是想开设自己的餐厅(创建服务器),还是想去别的餐厅点菜(发出请求),HTTP模块都能满足你的需求。
118 18
|
7月前
|
JavaScript 前端开发 数据可视化
【01】Cocos游戏开发引擎从0开发一款游戏-cocos环境搭建以及配置-Cocos Creator软件系统下载安装-node环境-优雅草卓伊凡
【01】Cocos游戏开发引擎从0开发一款游戏-cocos环境搭建以及配置-Cocos Creator软件系统下载安装-node环境-优雅草卓伊凡
374 2
【01】Cocos游戏开发引擎从0开发一款游戏-cocos环境搭建以及配置-Cocos Creator软件系统下载安装-node环境-优雅草卓伊凡
|
7月前
|
人工智能 JavaScript 关系型数据库
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
218 14
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
|
7月前
|
前端开发 JavaScript Java
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
318 13
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
|
7月前
|
SQL JavaScript 安全
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
262 11
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
|
7月前
|
人工智能 JavaScript 安全
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
266 13
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
|
JSON JavaScript 前端开发
Node.js学习笔记(九、模块系统)
Node.js学习笔记(九、模块系统)
225 0
Node.js学习笔记(九、模块系统)