浅析Node.JS的模块编译过程及__dirname等特殊变量在模块内的定义

在我们使用nodejs的开发工程中,我们可以使用  require 函数对模块进行引用,今天在开发过程中遇到了一些关于模块引用及模块内某些特殊变量的问题,查询了一些资料后写下了这篇文章。

由CommonJS模块规范我们可以知道在模块文件里存在了require、exports、module、__filename、__dirname这几个变量,但在模块的文件里并没有定义这几个变量,这些变量又是从哪里来的呢。

总所周知,编程时我们有可能会遇到一种叫“污染全局变量”的情况存在。为了防止模块的变量和把项目内的变量污染,Node在编译过程中对获取的JavaScript文件内容进行了头尾包装。

具体操作是在头部加入了

然后再尾部加入

通过在代码第一行写入错误内容,运行时的报错可以知道被包装了


在Node源代码中的实现方式:

(来自 node / lib/internal/bootstrap/loaders.js)


这样就相当于把模块代码包装在了一个闭包函数里,把全局的作用域和模组的作用域隔离开来,并且以参数的形式定义了require、exports、module、__filename、__dirname这五个变量。


我们可以测试下闭包函数的作用域对变量定义的影响

图中我们在闭包函数外部定义了一个VaribleOutSide变量,并且在闭包函数的参数里加入了VaribleOutSide和__dirname这两个参数

运行时在外部打印了__dirname和__filename;在闭包函数内部打印了VaribleOutSide、__dirname和__filename。

结果是在闭包函数内部打印的VaribleOutSide和__dirname为undefined,但是__filename扔打印出全局的变量。

这就是因为在闭包函数的参数上写了VaribleOutSide和__dirname这两个参数,但在调用这个闭包函数时并未提交任何参数,所以这两个变量在闭包函数内变为了未定义。

这相当于为VaribleOutSide和__dirname在闭包函数内清洗了之前的内容。


那问题又来了,既然闭包函数清洗了__dirname和__filename,为什么在模块内仍能使用两个变量并且值为该模块文件的地址呢?

在Node.JS对模块文件进行作用域隔离(包装)后,通过vm原生模块的runInThisContext()方法执行(类似evil,只确定上下文,执行的内容不污染全局),然后返回一个具体的function对象。然后将这个对象的exports属性、require()方法、module(模块对象自身)、以及在文件定位中得到的完整文件路径和文件目录作为参数传递给这个闭包函数的function()执行,并在执行之后,把这个对象的exports属性返回给调用方。

这就是为什么这些变量没有定义,但却可以在模块内存在的原因,也解释了为什么模块文件在外部只能通过exports接口访问(其余的任何方法和属性都无法访问)的原因。

自此,我们在前面提出的问题就已经全部解决了

 

Q:如何在模块里读取项目的根目录?

A:使用PATH模块进行读取

 

 

分享到:

0 条评论

昵称

This site uses Akismet to reduce spam. Learn how your comment data is processed.

与博主谈论人生经验?