模块是 Node.js 应用程序的基本组成部分,文件和模块是一一对应的。一个 Node.js 文件就是一个模块,这个文件可能是 JavaScript 代码、JSON 或者编译过的 C/C++ 扩展。

由于JavaScript没有模块系统,所以Node.js依靠CommonJS规范自身实现了模块系统。

模块的简单使用——exports 、require 和 module

在编写和使用每个模块时,Node.js都有require、exports、module三个预先定义好的变量可供使用。

  1. exports

    exports对象是当前模块的导出对象,用于导出模块公有方法和属性。

    事实上,exports 本身仅仅是一个普通的空对象,即 {},它专门用来声明接口。例如:

    //module.js
    exports.sayHello = function(name) {
    console.log('Hello ' + name);
    };
  2. require

    require函数用于在当前模块中加载和使用别的模块,传入一个模块名,返回一个模块导出对象。模块名可使用相对路径(以./开头),或者是绝对路径(以/或C:之类的盘符开头)。另外,模块名中的.js扩展名可以省略。例如:

    //index.js
    var myModule = require('./module');
    myModule.sayHello("node");
  3. module

    通过module对象可以访问到当前模块的一些相关信息,但最多的用途是覆盖 exports。例如模块导出对象默认是一个普通对象,如果想改成一个函数的话:

    //module.js
    module.exports = function(name) {
    console.log('Hello ' + name);
    }; //index.js
    var sayHello = require('./module');
    sayHello("node");

模块进阶——模块载入策略

Node.js的模块分为两类,一类为原生(核心)模块,一类为文件模块。原生模块在Node.js源代码编译的时候编译进了二进制执行文件,加载的速度最快。另一类文件模块是动态加载的,加载速度比原生模块慢。

内部实现机制

加载文件模块的工作,主要由原生模块module来实现和完成,该原生模块在启动时已经被加载,进程直接调用到runMain静态方法。

Module源码:

function Module(id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
if (parent && parent.children) {
parent.children.push(this);
} this.filename = null;
this.loaded = false;
this.children = [];
}

模块解析流程:

  1. 命令行执行主模块

    命令行执行主模块

    // bootstrap main module.
    Module.runMain = function() {
    // Load the main module--the command line argument.
    Module._load(process.argv[1], null, true);
    // Handle any nextTicks added in the first tick of the program
    process._tickCallback();
    };
  2. 处理模块

    Module.runMain方法会在最后执行_load静态方法,该方法又会在分析文件名之后执行:

    //实例化Module函数
    var module = new Module(id, parent);

    并根据文件路径缓存当前模块对象,该模块实例对象则根据文件名加载。

    module.load(filename);

    这时,Node.js会根据不同文件模块类型的后缀名来决定加载方法。

    • js:通过fs模块同步读取js文件并编译执行。
    • node:通过C/C++进行编写的Addon。通过dlopen方法进行加载。
    • json:读取文件,调用JSON.parse解析加载。
  3. 输出结果

    最后js文件形式的模块会变成以下形式的内容:

    (function (exports, require, module, __filename, __dirname) {
    var circle = require('./circle.js');
    console.log('The area of a circle of radius 4 is ' +circle.area(4));
    });

    所以此时,主模块内可以使用exports, require, module等变量了,而其他模块又会通过require引进主模块,require方法会同runMain一样调用_load静态方法,以此类推。

    require源码:

    // 传入模块路径作为参数. 返回 模块的exports属性.
    
    Module.prototype.require = function(path) {
    assert(path, 'missing path');
    assert(typeof path === 'string', 'path must be a string');
    return Module._load(path, this, /* isMain */ false);
    };

Q&A

  1. console.log(this)在浏览器和Node中分别打印出什么?

    答:显然浏览器中直接打印this指向Window对象,而在Node中,我们编写的文件其实外面都包裹了一层函数,而且该函数执行时强制apply将this指向了module.exports,因此此处打印为{}。

  2. 为什么require、__filename、__dirname、module、exports等几个变量并没有定义在app.js 文件中,但是这个方法却存在的原因。

    答:这里提到的所有属性均是_load方法中我们编写的js文件外层包裹函数提供给我们的,因此可以直接调用。

Node.js学习笔记(二):模块的更多相关文章

  1. Node.js学习笔记(二) --- CommonJs和Nodejs 中自定义模块

    一. 什么是 CommonJs? JavaScript 是一个强大面向对象语言,它有很多快速高效的解释器. 然而, JavaScript标准定义的 API 是为了构建基于浏览器的应用程序.并没有制定一 ...

  2. [转]node.js学习笔记(二)

    二.express 1.安装 express4 npm --registry=http://registry.npmjs.org install -g express-generator (全局) 2 ...

  3. 04 Node.js学习笔记之模块的加载

    A文件代码: //1.require是一个方法,它的作用就是用来加载模块的 console.log("执行 B ") require('./b.js'); console.log( ...

  4. Node.js学习笔记(2):基本模块

    Node.js学习笔记(2):基本模块 模块 引入模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在No ...

  5. 一点感悟:《Node.js学习笔记》star数突破1000+

    写作背景 笔者前年开始撰写的<Node.js学习笔记> github star 数突破了1000,算是个里程碑吧. 从第一次提交(2016.11.03)到现在,1年半过去了.突然有些感慨, ...

  6. 系列文章--Node.js学习笔记系列

    Node.js学习笔记系列总索引 Nodejs学习笔记(一)--- 简介及安装Node.js开发环境 Nodejs学习笔记(二)--- 事件模块 Nodejs学习笔记(三)--- 模块 Nodejs学 ...

  7. Node.js学习笔记(1):Node.js快速开始

    Node.js学习笔记(1):Node.js快速开始 Node.js的安装 下载 官方网址:https://nodejs.org/en/ 说明: 在Windows上安装时务必选择全部组件,包括勾选Ad ...

  8. Node.js学习笔记(3):NPM简明教程

    Node.js学习笔记(3):NPM简明教程 NPM常用操作 更新NPM版本 npm install npm -g -g,表示全局安装.我们可以指定更新版本,只需要在后面填上@版本号即可,也可以输入@ ...

  9. Node.js学习笔记(4):Yarn简明教程

    Node.js学习笔记(4):Yarn简明教程. 引入Yarn NPM是常用的包管理工具,现在我们引入是新一代的包管理工具Yarn.其具有快速.安全.可靠的特点. 安装方式 使用npm工具安装yarn ...

随机推荐

  1. IntelliJ IDEA 发布13版本——创造java奇迹

    IntelliJ IDEA被公认为业界最好的Java开发平台.此次发布的了13版本,更是集合了与Java EE.Android.Spring.Scala和Gradle最新合作与支持. Java EE  ...

  2. Retrofit相关资料

    高速Android开发系列网络篇之Retrofithttp://www.w3c.com.cn/%E5%BF%AB%E9%80%9Fandroid%E5%BC%80%E5%8F%91%E7%B3%BB% ...

  3. PLSQL配置登录用户信息

    PLSQL配置登录用户信息 2012-08-30 08:47:02     我来说两句      作者:lsxy117 收藏    我要投稿 PLSQL配置登录用户信息   工作中经常使用PLSQL ...

  4. 基于.NET Socket Tcp的发布-订阅框架

    基于.NET Socket Tcp的发布-订阅框架 一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已 ...

  5. 一步一步实现基于Task的Promise库(二)all和any方法的设计和实现

    在上一篇中我们已经初步完成了Task类,如果仅仅是这些,那么没有多大意义,因为网上这类js库有很多,现在我们来些更复杂的使用场景. 如果我们现在有这样一个需求:我们要先读取aa.txt的内容,然后去后 ...

  6. Windows上memcached的使用

    Memcached是什么?Memcached是由Danga Interactive开发的,高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问速度. Memcached能缓存什 ...

  7. AngularJS中数据双向绑定(two-way data-binding)

    1.切换工作目录 git checkout step-4 #切换分支,切换到第4步 npm start #启动项目 2.代码 app/index.html Search: <input ng-m ...

  8. C#打包应用程序

    摘要:本文介绍在C#中手把手教你用C#打包应用程序(安装程序卸载程序) 1:新建安装部署项目 打开VS,点击新建项目,选择:其他项目类型->安装与部署->安装向导(安装项目也一样),然后点 ...

  9. CLR 的执行模型(2)

    第一章 CLR 的执行模型(2) 本篇内容大纲 Framework 类库(Framework Class Library , FCL) 通用类型系统(Common Type System,CTS) 公 ...

  10. Word2Vec在中文的应用

    google最近新开放出word2vec项目,该项目使用deep-learning技术将term表示为向量,由此计算term之间的相似度,对term聚类等,该项目也支持phrase的自动识别,以及与t ...