深入了解Node.js中的VM模块
VM模块是NodeJS里面的核心模块,支撑了require方法和NodeJS的运行机制,我们有些时候可能也要用到VM模板来做一些特殊的事情。本篇文章就来带大家详细了解下Node中的VM模块,希望对大家有所帮助!
参考文献 vm 虚拟机 | Node 官网
http://nodejs.cn/api/vm.html
在上一篇文章中,我们提到了一个问题。
字符串如何能变成 JS 执行呢?
我们详细介绍了两种方法,分别是 eval函数 和 new Function 。
在这里我们需要再强调一下, 由 Function
构造器创建的函数不会创建当前环境的闭包,它们总是被创建于全局环境,因此在运行时它们只能访问全局变量和自己的局部变量,不能访问它们被 Function
构造器创建时所在的作用域的变量。这一点与使用 eval
执行创建函数的代码不同。
global.a = 100; // 挂在到全局对象global上 var b = 200; // this !== global new Function("console.log(a)")() // 100 new Function("console.log(b)")() // b is not defined
Function
可以获取全局变量,所以他还是可能会有变量污染的情况出现。Function
是 模块引擎的实现原理 ,后续我会出一篇文章进行单独讲解。
还有一种解决方案,我们在上一次文章中没有进行详细的展开,那就是 vm
模块 。
vm模块
在上述文字中,我一直在强调一个概念,那就是 变量的污染。
VM的特点就是不受环境的影响,也可以说他就是一个 沙箱环境 (沙箱模式给模块提供一个环境运行而不影响其它模块和它们私有的沙箱)。
const vm = require('vm') global.a = 100; // 运行在当前环境中[当前作用域] vm.runInThisContext('console.log(a)'); // 100 // 运行在新的环境中[其他作用域] vm.runInNewContext('console.log(a)'); // a is not defined
在这里我们要强调一下,因为 在Node.js中全局变量是在多个模块下共享的,所以尽量不要在global中定义属性。 Demo中的定义是为了方便理解。
假设我们在同级目录下有一个文件 1.js ,里面定义了 global.a = 100;
。 现在我们引入这个文件
requrie(./1); console.log(a); // 100
我们可以发现,在当前文件中我们并没有定义变量a,仅仅只是把两个模块文件关联在了一起。这就是我上面提到的,Node中全局变量是在多个模块下共享的。
他的原理是因为在 Node 的环境中,全局中有一个执行上下文。
// 模拟一下Node的全局环境 // vm.runInThisContext在当前全局环境执行,但不会产生新函数 - function(exports, module, require, __dirname, __filename){ // ... } - vm.runInThisContext ... // vm.runInNewContext在全局环境之外执行 vm.runInNewContext ...
所以,vm.runInThisContext
可以访问到 global
上的全局变量,但是访问不到自定义的变量。而 vm.runInNewContext
访问不到 global
,也访问不到自定义变量,他存在于一个全新的执行上下文。
而我们require
就是通过 vm.runInThisContext
实现的。
实现require
主要可以分为以下四步。
读取需要引入的文件。
读取到文件后,将代码封装成一个函数。
通过
vm.runInThisContext
将他转变成 JS 语法。代码调用。
假设我们现在有以下两个文件。分别是 a.js 和 b.js
// 文件a通过module.exports导出一个变量,在文件b中使用require进行接收。 // a.js module.exports = "a" // b.js let a = require('./a'); console.log(a); // a
我们可以通过上面的四个步骤,分析一下导入导出的实现逻辑是什么样的。
读取文件。
将需要引入的文件内容引入到需要接收的文件里,就会变成这个样子
let a = module.exports = "a";
登录后复制但是这种形式,Node根本解析不了,所以我们就需要进行第二步。
将读取的文件封装成函数。
let a = (function(exports, module, require, __dirname, __filename){ module.exports = "a"; return module.exports })(...args) // exports, module, require, __dirname, __filename 将五个参数传入
登录后复制封装成函数的原因,我们可以参考下面这个例子。
假设我们现在传入的不是字符串,而是一个函数。
// a.js var a = 100; module.exports = function(){}
登录后复制这样我们在解析的时候,就会被解析成下面这种格式
let a = (function(exports, module, require, __dirname, __filename){ var a = 100; module.exports = function(){}; return module.exports })(...args) // exports, module, require, __dirname, __filename 将五个参数传入
登录后复制我们导出的是
module.exports
,所以在模块文件中定义的变量a,也只属于当前这个执行上下文。在解析的时候,变量a 会被放到函数中。真正的实现了 作用域分离。
-
vm.runInThisContext
解析成可执行的Js代码我们处理过的代码会以字符串的形式存在,所以我们需要通过
vm.runInThisContext
将字符串进行解析。 -
进行代码调用
在此之前,我们其实还需要对代码进行调试。
更多node相关知识,请访问:nodejs 教程!!
以上是深入了解Node.js中的VM模块的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题











基于无阻塞、事件驱动建立的Node服务,具有内存消耗低的优点,非常适合处理海量的网络请求。在海量请求的前提下,就需要考虑“内存控制”的相关问题了。 1. V8的垃圾回收机制与内存限制 Js由垃圾回收机

选择一个Node的Docker镜像看起来像是一件小事,但是镜像的大小和潜在漏洞可能会对你的CI/CD流程和安全造成重大的影响。那我们如何选择一个最好Node.js Docker镜像呢?

文件模块是对底层文件操作的封装,例如文件读写/打开关闭/删除添加等等 文件模块最大的特点就是所有的方法都提供的**同步**和**异步**两个版本,具有 sync 后缀的方法都是同步方法,没有的都是异

Node 19已正式发布,下面本篇文章就来带大家详解了解一下Node.js 19的 6 大特性,希望对大家有所帮助!

事件循环是 Node.js 的基本组成部分,通过确保主线程不被阻塞来实现异步编程,了解事件循环对构建高效应用程序至关重要。下面本篇文章就来带大家深入了解Node中的事件循环 ,希望对大家有所帮助!

如何用pkg打包nodejs可执行文件?下面本篇文章给大家介绍一下使用pkg将Node项目打包为可执行文件的方法,希望对大家有所帮助!
