3

JavaScript的模块机制—CommonJS规范

 1 year ago
source link: https://www.daguanren.cc/post/JavaScript_module_commonjs.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

JavaScript早期的"script"标签的代码引入方式显得杂乱无章,缺乏规范,不像Java有类文件,python有import机制,Ruby有require,PHP有include和require。

早期JavaScript的规范主要是ECMAScript,ECMAScript它主要包含词法、类型、上下文、表达式、声明、方法、对象等语言的基本要素。有如下缺陷:

  • 没有模块系统
  • 标准库较少,没有I/O流
  • 没有标准接口,如数据库之类的统一接口
  • 缺乏包管理系统

经过后期的发展,JavaScript不断被类聚和抽象,终于有社区为JavaScript制定了规范,其中一个著名的规范就是CommonJS规范。

CommonJS规范

CommonJS的提出为JavaScript覆盖如下内容的规范:

  • 模块、二进制、Buffer、字符集编码、I/O流
  • 进程环境、文件系统、套接字、单元测试、Web服务器网关接口、包管理
alt

Node借鉴CommonJS的Modules实现了一套非常易用的模块系统,NPM借鉴Packages规范。

CommonJS模块规范

CommonJS模块规范主要包括:模块引用、模块定义和模块标识。

1、 模块引用

var math = require('math');
2、模块定义
// math.jsexports.add = function(){ var sum = 0, i = 0, args = arguments, l =args.length; while(i < l){ sum += args[i++]; return sum;maxima
// program.jsvar math = require('math');exports.increment = function (val) { return math.add(val, 1);javascript

3、模块标识

模块标识就是给require()方法的参数,它必须是符号小驼峰命名的字符串,或者以.、..开头的相对路径,或者绝对路径。

每个模块具有独立的空间,互不干扰。

alt

用户不用考虑变量污染,命名空间等方案与之相比相形见绌。

命名空间是为了防止不同人编写类库发生命名冲突而设计的,使变量、函数名称、类名称在本空间和在其他空间都可以使用相同的名称。就好比不同的文件夹下可以有相同的文件名一样,但在相同的文件夹下不能有重复的文件名,命名空间就好比这个虚拟的文件夹。

一个是水果的table

<fruit:table><fruit:tr><fruit:td>Pear</h:td><fruit:td>Apple</h:td></fruit:tr></fruit:table>xml

一个是桌子的table:

<wood:table><wood:name>Desk</f:name><wood:width>110</f:width><wood:length>90</f:length></wood:table>xml

Node的模块实现

在Node中引入模块,有如下3个步骤:

Node中的模块分为两类:核心模块(Node提供的模块,如http、fs、path)和文件模块(用户编写的模块)。

  • 核心模块在Node源代码的编译过程中,直接编译进了二进制文件。在Node进程启动时,部分核心模块就被直接加载进内存中,所以这部分核心模块引入时,稳健定位和编译执行两个步骤省略掉了,并且在路径分析中有限判断,所以加载速度最快。
  • 文件模块则是在运行时动态加载,需要完整的路径分析、文件定位、编译执行的过程,速度比核心模块慢。

Node对引入过得模块会进行缓存,缓存的是编译和执行之后的对象。

require方法对相同模块的二次加载一律采用缓存优先的方式。

模块路径是按照:

  • 当前文件目录下的node_modules目录
  • 父目录下的node_modules目录
  • 逐级向上,直到跟目录下的node_modules目录

文件不同的扩展名,处理方式不同:

  • .js文件。通过fs模块同步读取文件后编译执行
  • .node文件。这是C/C++编写的扩展文件,通过dlopen()方法加载最后编译生成的文件。
  • .json文件。通过fs模块同步读取文件后,用JSON.parse()解析返回的结果。
  • 其余扩展名文件当做.js文件处理。

我们知道每个模块文件中存在require、exports、module这3个变量,但它们在模块文件中并没有定义,那么从何而来?另外每个模块中还有_filename、_dirname这两个变量。

事实上,在编译的过程中,Node对获取的JavaScript文件内容进行了头尾包装。在头部增加了

(function(exports, require, module, _filename, _dirname){

这样每个模块文件之间都进行了作用域的隔离。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK