《深入浅出Node.js》第2章 模块机制
@by Ruth92(转载请注明出处)
第2章 模块机制
JavaScript 先天缺乏的功能:模块。
一、CommonJS 规范:
JavaScript 规范的缺陷:1)没有模块系统;2)标准库较少;3)没有标准接口;4)缺乏包管理系统。
CommonJS 规范的提出,主要是为了弥补当前 JavaScript 没有标准的缺陷,使其具备开发大型应用的基础能力。
Node 借鉴 CommonJS 的 Modules 规范实现了一套非常易用的模块系统,NPM 对 Packages 规范的完好支持使得 Node 应用在开发过程中事半功倍。
☛ 【CommonJS 对模块的定义】:
模块引用:
require()
方法:引入一个模块的 API 到当前上下文中。var math = require('math');
模块定义:
module
对象:代表模块自身。exports
对象:module
的属性,用于导出当前模块的方法或者变量,且它是唯一导出的出口。在 Node 中,一个文件就是一个模块,将方法挂载在
exports
对象上作为属性即可定义导出的方式:// math.js
exports.add = function() {};
模块标识:传递给
require()
方法的参数
♫ 【优点】:CommonJS 构建的这套模块导出和引入机制使得用户完全不必考虑变量污染,命名空间等方案与之相比相形见绌。
二、Node 的模块实现
在 Node 中引入模块,需要经历:(1)路径分析;(2)文件定位;(3)编译执行。
Node 中的模块分为两类:
(1)核心模块:在 Node 源代码编译过程中,编译进了二进制执行文件,所以在引入时,可以省略文件定位和编译执行,且在路径分析中优先判断,加载速度最快。
(2)文件模块:在运行时动态加载,需要完整的3步过程,速度较慢。
☛ 【详细的模块加载过程】:
优先从缓存加载(第一优先级)
♫ 提高性能的方式比较:
- 前端浏览器:缓存静态脚本文件,仅缓存文件。
- Node:对引入过的模块进行缓存,缓存的是编译和执行之后的对象。
无论是核心模块还是文件模块,
require()
方法对相同模块的二次加载都一律采用缓存优先的方式。核心模块的缓存检查先于文件模块。
路径分析与文件定位
① 模块标识符分析:
require()
方法的参数- 核心模块:如 http、fs、path 等。(最快)
- 路径形式的文件模块:
.
或..
开始的相对路径文件模块 和 以/
开始的绝对路径文件模块。 - 自定义模块:特殊的文件模块,可能是一个文件或者包的形式。(最慢)
模块路径
:Node 在定位文件模块的具体文件时指定的查找策略,具体表现为一个路径组成的数组。- 当前文件的路径越深,模块查找越耗时,因此自定义模块加载速度最慢。
② 文件定位
- 文件扩展名分析
require()
在分析标识符的过程中,会出现标识符不包含扩展名的情况;- Node 会按
.js
、.json
、.node
的次序依次补足扩展名,调用fs
模块同步阻塞式地依次尝试。 - 缓解 Node 单线程中阻塞式调用的诀窍:(1) 带上扩展名;(2) 同步配合缓存。
- 目录分析和包
- 在分析标识符时,
require()
通过分析文件扩展名之后,可能没有查找到对应文件,但却得到一个目录(经常出现在引入自定义模块和逐个模块路径进行查找时),此时 Node 会将目录当做一个包来处理。
- 在分析标识符时,
模块编译
在 Node 中,每个文件模块都是一个对象,其定义如下:
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 = [];
}
定位到具体的文件后,Node 会新建一个模块对象,然后根据路径载入并编译。
对于不同的文件扩展名,有不同的载入方法:
.js
文件:通过fs
模块同步读取文件后编译执行;.node
文件:这是用c/C++
编写的扩展文件,通过dlopen()
方法加在最后编译生成的文件;.json
文件:通过fs
模块同步读取文件后,用JSON.parse()
解析返回结果;- 其余扩展名文件:它们都被当做
.js
文件载入。
每一次编译成功的模块都会将其文件路径作为索引缓存在
Module._cache
对象上,以提高二次引入的性能。exports
只能改变形参的引用,但不能改变作用域外的值;要达到require
引入一个类的效果,请赋值给module.exports
对象。
三、核心模块
核心模块分为两部分: c/C++ 编写的和 JavaScript 编写的。
JavaScript 核心模块的编译过程
① 转存为 C/C++ 代码
② 编译 JavaScript 核心模块
与文件模块的区别:
- 获取源代码的方式(核心模块是从内存中加载的);
- 缓存执行结果的位置:编译成功的模块缓存到 NativeModule._cache 对象上,文件模块则缓存到 Module._cache 对象上。
C/C++ 核心模块的编译过程
内建模块:由纯 C/C++ 编写的部分,Node 的
buffer
、crypto
、evals
、fs
、os
等。内建模块的优势:(1)性能上优于脚本语言;(2)在进行文件编译时,被编译进二进制文件,一旦 Node 开始执行,它们被直接加载进内存中,无须再做标识符定位、文件定位、编译等过程,直接就可执行。
➹ 【内建模块的导出】:
在 Node 的所有模块类型中,存在着一种依赖层级关系:
通常,不推荐文件模块直接调用内建模块。如需调用,直接调用核心模块即可,因为核心模块中基本都封装了内建模块。
- Node 在启动时,会生成一个全局变量
process()
,并提供Binding()
方法来协助加载内建模块。
核心模块的引入流程
编写核心模块
- 核心模块中的 JavaScript 部分几乎与文件模块的开发相同,遵循 CommonJS 规范,上下文中除了拥有
require
、module
、exports
外,还可以调用 Node 中的一些全局变量; - 内建模块的编写通常分成两步:编写头文件和编写 C/C++ 文件。
- 核心模块中的 JavaScript 部分几乎与文件模块的开发相同,遵循 CommonJS 规范,上下文中除了拥有
四、C/C++ 扩展模块
- C/C++ 扩展模块与 JavaScript 模块的区别在于加载之后不需要编译,直接执行之后就可以被外部调用了,其加载速度比 JavaScript 模块略快。
- 使用 C/C++ 扩展模块的好处:可更灵活和动态地加载它们,保持 Node 模块自身简单性的同时,给予 Node 无限的可扩展性。
五、模块调用栈
- C/C++ 内建模块属于最底层的模块,它属于核心模块,主要提供 API 给 JavaScript 核心模块和第三方JavaScript 文件模块调用。
- javaScript 核心模块属于的两类职责:1)作为C/C++内建模块的封装层和桥接层,供文件模块调用;2)纯粹的功能模块,它不需要跟底层打交道,但是又十分重要。
六、包与 NPM
在模块之外,包和 NPM 是将模块联系起来的一种机制。
- CommonJS 的包规范由两部分组成:包结构、包描述文件。
- CommonJS 包规范是理论,NPM 是其中的一种实践。
➹ NPM 常用功能
七、前后端共用模块
模块的侧重点
浏览器端的 JavaScript:需要经历从同一个服务器端分发到多个客户端执行,瓶颈在于带宽,需要通过网络加载代码;
服务器端的 javaScript:相同的代码需要多次执行,瓶颈在于 CPU 和内存等资源,从磁盘加载。
AMD 规范
产生原因:鉴于网络的原因,CommonJS 为后端 JavaScript 制定的规范并不完全适合前端的应用场景。
AMD:Asynchronous Module Definition,异步模块定义,是 CommonJS 规范的一个延伸,适用于前端应用场景。
AMD 模块定义(id 和依赖是可选的):
// 定义
define(id?, dependencies?, factory); // 实现
define(function() {
var exports = {};
exports.sayHello = function() {
alert('Hello from module: ' + module.id);
};
return exports;
})
与 Node 的区别:1)AMD模块需要用
define
来明确定义模块,而在 Node 实现中是隐式包装的,目的都是进行作用域隔离;2)内容需要通过返回的方式实现导出。
CMD 规范
由国内的玉伯提出。
与 AMD 规范的主要区别:在于定义模块和依赖引入的部分。
/**
* AMD 模块定义:
* 在声明模块的时候需要制定所有的依赖,通过形参传递到模块内容中
*/
define(['dep1', 'dep2'], function(dep1, dep2) {
return function () {};
}); /**
* CMD 模块定义:
* 更加接近于 Node 对 CommonJS 规范的定义
*/
define(factory); // 在依赖部分,CMD 支持动态引入
// require、exports 和 module 通过形参传递给模块
// 在需要依赖模块时,随时调用 require() 引入即可
define(function(require, exports, module) {
// The module code goes here
});
兼容多种模块规范
- 为了让同一个模块运行在前后端提出的解决方案;
- 将类库代码包装在一个闭包内,兼容 Node、AMD、CMD 以及常见的浏览器环境。
《深入浅出Node.js》第2章 模块机制的更多相关文章
- 《深入浅出Node.js》第6章 理解 Buffer
@by Ruth92(转载请注明出处) 第6章 理解 Buffer ✁ 为什么需要 Buffer? 在 Node 中,应用需要处理网络协议.操作数据库.处理图片.接收上传文件等,在网络流和文件的操作中 ...
- 《深入浅出Node.js》第7章 网络编程
@by Ruth92(转载请注明出处) 第7章 网络编程 Node 只需要几行代码即可构建服务器,无需额外的容器. Node 提供了以下4个模块(适用于服务器端和客户端): net -> TCP ...
- 深入浅出Node.js(一):什么是Node.js
Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹.从在github的访问量超过Rails,到去年底Node.jsS创始人Ryan Dalh加盟Joyent获得企业资助,再到 ...
- 一个月时间整理《深入浅出Node.js》
今天终于把朴灵老师写的<深入浅出Node.js>给学习完了, 这本书不是一本简单的Node入门书籍,它没有停留在Node介绍或者框架.库的使用层面上,而是从不同的视角来揭示Node自己内在 ...
- 深入浅出Node.js(一):什么是Node.js(转贴)
以下内容转自:http://www.infoq.com/cn/articles/what-is-nodejs/ 作者:崔康 [编者按]:Node.js从2009年诞生至今,已经发展了两年有余,其成长的 ...
- 《深入浅出node.js(朴灵)》【PDF】下载
<深入浅出node.js(朴灵)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062563 内容简介 <深入浅出Node. ...
- 深入浅出Node.js(上)
(一):什么是Node.js Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹.从在github的访问量超过Rails,到去年底Node.jsS创始人Ryan Dalh加盟 ...
- 读书笔记: 深入浅出node.js
>> 深入浅出node.js node.js是c++编写的js运行环境 浏览器: 渲染引擎 + js引擎 后端的js运行环境 node.js用google v8引擎,同时提供很多系统级的A ...
- 深入浅出node.js游戏服务器开发1——基础架构与框架介绍
2013年04月19日 14:09:37 MJiao 阅读数:4614 深入浅出node.js游戏服务器开发1——基础架构与框架介绍 游戏服务器概述 没开发过游戏的人会觉得游戏服务器是很神秘的 ...
随机推荐
- Linux 下配置多机实时同步
没钱的时候,用此方案做网站内容的负载均衡.异地备份,经济实惠又方便(仅针对网站文件做实时同步,如果数据库,则考虑mysql的多主架构) 一.机器配置及机房IP A机,位于杭州,IP: 115.33.2 ...
- Installing a single-server IBM FileNet P8 Platform system with IBM Content Navigator
Product documentation Abstract You can install all of the IBM FileNet P8 Platform components on a si ...
- 编程思考 PetShop读后感
标准,插拔式的设计思想建立一致的标准是通向“复用”的通道.分层,使其得到的充分的独立.一个东西如果独立了[不是孤立],这个事物就具有很强大的力量,这个和一个人的成长是相同的道理.所以呢,在写程序的过程 ...
- 闭包 (循环事件获取不到i) 和 各种解决循环获取不到i的解决方法
for(var i in fav){ (function(){ var p=i; var obj=$S.getId(fav[i]); ...
- GCJ 2015-Qualification-B Infinite House of Pancakes 枚举,思路,误区 难度:3
https://code.google.com/codejam/contest/6224486/dashboard#s=p1 题目不难,教训记终生 题目给了我们两种操作:1 所有人都吃一个,简记为消除 ...
- ARM2440 LCD实验
1. S3C2440内部LCD控制器结构图: 我们根据数据手册来描述一下这个集成在S3C2440内部的LCD控制器: a:LCD控制器由REGBANK.LCDCDMA.TIMEGEN.VIDPRCS寄 ...
- mac 连接mysql提示 Warning: mysqli::real_connect(): (HY000/2002): No such file or directory
mac 连接mysql的时候提示 Warning: mysqli::real_connect(): (HY000/2002): No such file or directory [说明1]MAC下M ...
- dx wpf的各种坑
这篇随笔总结dx wpf使用中的各种坑,持续更新~ LookUpEdit里内嵌的DXGrid的名字必须是"PART_GridControl",不能不写.也不能写错.我对比了2个小时 ...
- Oracle性能调优
这部分目前主要是从网上搜集来的,后续要在实践中慢慢体会. v$sqltext: 存储的是被分割的sql v$sqlarea: 存储的是完整的sql和一些统计信息,比如累计的执行次数.逻辑读.物理读等( ...
- python操作二进制文件
有的时候需要用python处理二进制数据,比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用 struct来处理c语言中的结构体. struct模块中最重 ...