VM 모듈은 NodeJS의 핵심 모듈이며 NodeJS의 작동 메커니즘을 지원합니다. 때로는 VM 템플릿을 사용하여 몇 가지 특별한 작업을 수행할 수도 있습니다. 이 글이 Node의 VM 모듈에 대해 자세히 이해하는 데 도움이 되기를 바랍니다.
참조 vm 가상 머신 | Node 공식 홈페이지
http://nodejs.cn/api/vm.html
이전 기사에서 문제를 언급했습니다.
문자열을 JS로 변환하여 실행하려면 어떻게 해야 하나요?
2가지 방법, 즉 eval function과 new Function을 자세히 소개했습니다.
여기서 Function
생성자로 생성된 함수는 현재 환경의 클로저를 생성하지 않는다는 점을 다시 강조해야 합니다. 이 함수는 항상 전역 환경에서 생성되므로 런타임 전역에서만 액세스할 수 있습니다. 변수 및 자체 지역 변수는 함수
생성자에 의해 생성된 범위의 변수에 액세스할 수 없습니다. 이는 eval
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的特点就是不受环境的影响,也可以说他就是一个 沙箱环境 (沙箱模式给模块提供一个环境运行而不影响其它模块和它们私有的沙箱)。
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
생성 기능을 실행하는 코드가 다릅니다.
// 文件a通过module.exports导出一个变量,在文件b中使用require进行接收。 // a.js module.exports = "a" // b.js let a = require('./a'); console.log(a); // a
함수
는 전역 변수를 얻을 수 있으므로 여전히 변수 오염이 있을 수 있습니다. 함수
는 의 구현 원리입니다. 추후 별도로 설명하는 글을 게시하겠습니다.
vm
모듈입니다. 위 텍스트에서 저는 변수의 오염. VM의 특징은 환경에 영향을 받지 않는다는 점입니다. 샌드박스 환경이라고도 할 수 있습니다. (샌드박스 모드는 다른 모듈에 영향을 주지 않고 모듈이 실행될 수 있는 환경을 제공하며, ).
let a = module.exports = "a";
여기서 강조하고 싶은 점은
Node.js같은 디렉토리에 global.a = 100;
을 정의하는 1.js 파일이 있다고 가정해 보겠습니다. 이제 이 파일을 소개하므로(
let a = (function(exports, module, require, __dirname, __filename){ module.exports = "a"; return module.exports })(...args) // exports, module, require, __dirname, __filename 将五个参数传入
) 현재 파일에서 변수 a를 정의하지 않고 두 모듈 파일을 함께 연결한다는 것을 알 수 있습니다. 위에서 언급한 내용은 노드의 전역 변수가 여러 모듈에서 공유된다는 것입니다.
그 이유는
Node// a.js var a = 100; module.exports = function(){}
그래서 vm.runInThisContext
는 global
의 전역 변수에 액세스할 수 있지만 사용자 정의 변수에는 액세스할 수 없습니다. 그러나 vm.runInNewContext
는 전역
에 액세스할 수 없으며 사용자 정의 변수에도 액세스할 수 없습니다. 그리고 require
는 vm.runInThisContext
를 통해 구현됩니다.
require
구현은 다음 네 단계로 나눌 수 있습니다. 🎜🎜🎜🎜가져와야 하는 파일을 읽어보세요. 🎜🎜🎜🎜파일을 읽은 후 코드를 함수로 캡슐화하세요. 🎜🎜🎜🎜 vm.runInThisContext
를 통해 JS 구문으로 변환하세요. 🎜🎜🎜🎜코드콜. 🎜🎜🎜🎜이제 다음 두 파일이 있다고 가정합니다. 🎜a.js🎜 및 🎜b.js🎜🎜let a = (function(exports, module, require, __dirname, __filename){ var a = 100; module.exports = function(){}; return module.exports })(...args) // exports, module, require, __dirname, __filename 将五个参数传入
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 중국어 웹사이트의 기타 관련 기사를 참조하세요!