• 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:UsersusernameDesktopindex.js
     * C:UsersusernameDesktop
     * {}
     * 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]
     *     }
     *   }
     * }
    

    【 阅读更多 Node.js 系列文章,请看 Node.js学习笔记

    版权声明:本博客属于个人维护博客,未经博主允许不得转载其中文章。
  • 相关阅读:
    管道通讯
    C++类型转换
    自定义数组容器MyArray框架
    vue 转场动画
    身份证信息 获取年龄性别生日
    element ui +vue 输入 身份证号获取 性别 年龄 籍贯
    获取地理位置
    post 下载二进制pdf文件
    post 文件下载二进制流
    遍历对象的几中方法
  • 原文地址:https://www.cnblogs.com/wsmrzx/p/12236933.html
Copyright © 2020-2023  润新知