Modules

Stable

在 Node.js 模块系统中,每个文件都会被当做一个独立的模块。假设有一个名为 foo.js :

const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);

第一行中,foo.js加载了同文件夹下的模块circle.js.

·circle.js`中的内容:

const { PI } = Math;

exports.area = (r) => PI * r ** 2;

exports.circumference = (r) => 2 * PI * r;

模块circle.js 暴露了arear()circumfence() 方法。方法和对象通过对这个特殊的exports对象赋额外的属性值添加到了模块的根中。

模块的本地变量将是私有的,因为模块被Node.js封装在一个函数中。在本例中,变量PIcircle.js是私有的。

module.exports属性可以赋值一个新值(例如函数或对象)

下面,bar.js使用的是square模块,它导出一个Square类。

const Square = require('./square.js');
const mySquare = new Square(2);
console.log(`The area of mySquare is ${mySquare.area()}`);

square模块是在square.js中定义的

// assigning to exports will not modify module, must use module.exports
module.exports = class Square {
constructor(width) {
this.width = width;
} area() {
return this.width ** 2;
}
};

模块系统是在require('module')模块中实现的。

访问 main module

node.js直接运行的文件,require.main会被设置为这个文件的module. 由此,可以通过 require.main == module 判断当前文件是否被node.js直接运行 (via node *.js)。

因为module提供了一个filename属性(通常情况下等于__filename)。当前应用的入口可以通过require.main.filename获取到。

require 方法的实现

当调用 require() 方法时, 使用 require.resolve() 方法获取到准确的文件名称,逻辑如下:

require(X) from module at path Y
1. If X is a core module,
a. return the core module
b. STOP
2. If X begins with '/'
a. set Y to be the filesystem root
3. If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. THROW "not found" LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text. STOP
2. If X.js is a file, load X.js as JavaScript text. STOP
3. If X.json is a file, parse X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP LOAD_INDEX(X)
1. If X/index.js is a file, load X/index.js as JavaScript text. STOP
2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
3. If X/index.node is a file, load X/index.node as binary addon. STOP LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
a. Parse X/package.json, and look for "main" field.
b. let M = X + (json main field)
c. LOAD_AS_FILE(M)
d. LOAD_INDEX(M)
2. LOAD_INDEX(X) LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X) NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
a. if PARTS[I] = "node_modules" CONTINUE
b. DIR = path join(PARTS[0 .. I] + "node_modules")
c. DIRS = DIRS + DIR
d. let I = I - 1
5. return DIRS

Caching

modules 当第一次被加载时放到cache中。也就是说每次调用 require('foo') 时,如果解析到同一个文件,将会得到同一返回对象。

如果想要让一个module多次执行代码,export 一个 function, 然后调用那个 function

Module Caching 警告

module基于他们被解析的文件名加载到cache中。由于module可能因为调用module的路径不同而被resolve不同的文件名,所以并不保证每次调用同一个 require(module) 都会返回同一个对象。

对于大小写敏感的文件系统或者操作系统,即使module被解析为同一个文件,但是因为大小写的原因,module还是会多次加载。

Core Modules

Node.js 有一些module被编译为二进制文件,这些module在 Node.js 源文件中定义,位于lib/目录下面。

Core Module 一直被预加载在cache中。require('http') 将会调用内置的 HTTP module,即使当前目录下面有个名为 http 的文件

Cycles

a.js:

console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

b.js:

console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');

main.js:

console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done = %j, b.done = %j', a.done, b.done);

output:

$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true

为了防止无穷循环,一个 a.js unfinished copy 会返回给 b.js

File Modules

如果没有找到指定名称的问价,Node.js 会尝试添加文件后缀名 .js,.json,.node

都没有找到会返回 MODULE_NOT_FOUND

Folders as Modules

推荐把 programs 和 libraries 组织进一个目录下, 并提供一个统一的入口。有三种方式可以把文件夹的名字作为 require() 方法的参数传送。

  • 在文件夹根目录下创建一个 package.json 的文件,并指定 main mudule。
{
"name" : "some-library",
"main" : "./lib/some-library.js"
}
  • 创建一个index.js的文件作为入口
  • 创建一个index.node的文件作为入口

Loading from node_modules Folders

如果给 require() 传递的 module 名称不是一个 core module, 并且没有以 '/','../','./'作为开始, 那么Node.js 会尝试从 node_modules 目录下面查找所需要的module。

举例来说:有一个位于 ''/home/ry/projects/foo.js'' 调用 require('bar.js'), node.js 回按照下面的方式顺序查找 bar.js

  • /home/ry/projects/node_modules/bar.js
  • /home/ry/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js

    此外,可以使用已知路径的 module 作为参考,去查找相对于这个 module 的 其他 module;

Loading from the global folders

如果在 node_modules 里仍然没有找到指定的module,Node.js 会搜索系统环境变量 NODE_PATH 指定的目录。当然,前提是设置了这个环境变量。

墙裂推荐把相关 module 放在 node_modules目录下面,这样加载会更快更稳定。

The module wrapper

当一个module里面的代码被执行之前,Node.js 会先把module里面的代码封装起来:

(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});

这样做的优点是:

  • 会把变量限定在这个module里面,而不是global对象下面。
  • 提供一些貌似global的对象。(export,'require,'module','__filename,'__dirname`)

The module scope

__dirname (type:string)

Example: 运行node example.js from /Users/mjr

console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr

__filename(type:string)

Example: 运行node example.js from /Users/mjr

console.log(__filename);
// Prints: /Users/mjr/example.js
console.log(__dirname);
// Prints: /Users/mjr

给定两个文件a.js 和 b.js , a.js 依赖 b.js, 将会有如下的结构

  • /Users/mjr/app/a.js
  • /Users/mjr/app/node_modules/b/b.js

exports

等同于 module.exoports

module (type:object)

当前module

require() (type:function)

用于调用依赖module

require.cache(type:object)

所有通过require方法调用过的依赖都存储于这个对象当中。如果删除了对象中的某个Key,下一次调用 require() 方法时会重新加载这个module。但是这个方法不适用 native addons.

require.extensions(弃用)

require.main(type:object)

代表程序启动时的入口module.

require.resolve(request[这个是参数])

使用 require的机制去查找某个 module 的路径,但是只返回解析到的 filename, 而且不加载这个module.

require.resolve.paths(request[这个也是参数])

返回查找某个module遍历的路径信息(type:string[] | )

如果是core module, 返回 null

The module Object

module.children:当前module所有的依赖项

module.exports:可以用来赋值某个类的实例(exports 不可以)

a.js:

const EventEmitter = require('events');
module.exports = new EventEmitter();
// Do some work, and after some time emit
// the 'ready' event from the module itself.
setTimeout(() => {
module.exports.emit('ready');
}, 1000);

b.js:

const a = require('./a');
a.on('ready', () => {
console.log('module "a" is ready');
});

赋值操作需要马上完成,不可以在会掉中完成。下面的代码是不会有效的:

x.js:

setTimeout(() => {
module.exports = { a: 'hello' };
}, 0);

y.js:

const x = require('./x');
console.log(x.a);

其他属性

module.filename:<string> 全文件名

module.id:<string> module 标识 通常是全文件名

module.loaded:<boolean> module是否已加载

module.parent:<module> 第一个调用这个module 的 module

module.paths:<string[]> 搜索路径

module.require(id):module外调用require的方法

源链接

Node.js v10.1.0 Documentation的更多相关文章

  1. Node.js v7.4.0 Documentation Addons

    https://nodejs.org/docs/latest/api/addons.html Node.js Addons are dynamically-linked shared objects, ...

  2. node.js 在 Express4.0 框架使用 Connect-Busboy 实现文件上传

    node.js下四种post提交数据的方式 今天说分享的是其中一种,就是上传文件. Express 4.0 以后,将功能原子化,高内聚,低耦合,独立出了很多中间件 今天主要分享文件上传 对于conne ...

  3. npm dose not support Node.js v10.15.3

    事件起因: 楼主在vue-cli官网,尝试使用vue-cli3脚手架+yarn包管理器构建项目时,命令行窗口提示node版本不对.如下图 这个大家都知道该如何去解决,直接去node官网下载符合版本的n ...

  4. Node.js & module system

    Node.js & module system Node.js v10.9.0 Documentation https://nodejs.org/api/modules.html#module ...

  5. 《Node.js核心技术教程》学习笔记

    <Node.js核心技术教程>TOC \o "1-3" \h \z \u 1.章模块化编程 2019.2.19 13:30' PAGEREF _101 \h 1 08D ...

  6. Node.js 多线程完全指南

    [原文] 很多人都想知道单线程的 Node.js 怎么能与多线程后端竞争.考虑到其所谓的单线程特性,许多大公司选择 Node 作为其后端似乎违反直觉.要想知道原因,必须理解其单线程的真正含义. Jav ...

  7. 性能追击:万字长文30+图揭秘8大主流服务器程序线程模型 | Node.js,Apache,Nginx,Netty,Redis,Tomcat,MySQL,Zuul

    本文为<高性能网络编程游记>的第六篇"性能追击:万字长文30+图揭秘8大主流服务器程序线程模型". 最近拍的照片比较少,不知道配什么图好,于是自己画了一个,凑合着用,让 ...

  8. Node.js 0.12: 正确发送HTTP POST请求

    Node.js 0.12: 正确发送HTTP POST请求 本文针对版本:Node.js 0.12.4 之前写过一篇Node.js发送和接收HTTP的GET请求的文章,今天再写一篇,讲发送POST的请 ...

  9. 玩儿转物联网IoT - 在Beagle Bone Black上运行node.js 程序

    物联网(IoT)技术方兴未艾,智能手环,智能血压计,智能眼镜甚至智能鞋垫都开始进入我们的生活,各种智能设备层出不穷,世界已经到了一个"人有多大胆,地有多大产"的时代,不玩儿点物联网 ...

随机推荐

  1. NO.1 You must restart adb and Eclipse多种情形分析与解决方式

    一:错误提示 The connection to adb is down, and a severe error has occured. You must restart adb and Eclip ...

  2. onblur 对象失去焦点事件

    onblur 对象失去焦点事件 一.总结 1.几乎所有的控件都支持onblur事件 二.简介 onblur 事件 Event 对象 定义和用法 onblur 事件会在对象失去焦点时发生. 语法 onb ...

  3. thinkphp最简单路由

    thinkphp最简单路由 一.总结 1.路由应用场景(前台要,后台不要):前台所有人都可以看,所以前台的话设置路由,后台的话并不是所有人都进去,所以不需要设置路由 2.模块分离来实现路由场景应用:前 ...

  4. java DSA Signature Sign And Verify

    SignatureSignAndVerify import java.security.KeyPair; import java.security.KeyPairGenerator; import j ...

  5. Vue Cli 打包之后静态资源路径不对的解决方法

    cli2版本: 将 config/index.js 里的 assetsPublicPath 的值改为 './' . build: { ... assetsPublicPath: './', ... } ...

  6. 洛谷 P1308 统计单词数

    P1308 统计单词数 题目描述 一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数. 现在,请你编程实现这一功能,具体要求是:给定 ...

  7. 2.lombok系列2:lombok注解详解

    转自:https://www.imooc.com/article/18157 开篇 看到第一篇<初识lombok>你可能意犹未尽,本文我们按照场景来介绍一下常用的注解. 未特别说明,均标注 ...

  8. 2. Spring Boot Controller

    转自:https://blog.csdn.net/catoop/article/details/50501676

  9. 5.decltype类型拷贝

    #include <iostream> using namespace std; template <class T> void show(T *p) { //初始化 decl ...

  10. swiper轮播控件配置项

    var mySwiper = new Swiper ('.swiper-container', {    direction: 'horizontal',    loop: true,    auto ...