Maison  >  Article  >  interface Web  >  Une plongée approfondie dans le module VM dans Node.js

Une plongée approfondie dans le module VM dans Node.js

青灯夜游
青灯夜游avant
2021-11-22 19:36:183461parcourir

Le module VM est le module principal de NodeJS. Il prend en charge la méthode require et le mécanisme de fonctionnement de NodeJS. Parfois, nous pouvons également utiliser des modèles de VM pour effectuer certaines choses spéciales. Cet article vous donnera une compréhension détaillée du module VM dans Node J'espère qu'il vous sera utile !

Une plongée approfondie dans le module VM dans Node.js

Référence machine virtuelle vm | Site officiel de Node

http://nodejs.cn/api/vm.html

Dans l'article précédent, nous avons mentionné un problème.

Comment une chaîne peut-elle être transformée en JS pour exécution ?

Nous avons présenté deux méthodes en détail, à savoir eval function et new Function.

Nous devons souligner à nouveau ici que les fonctions créées par le constructeur Function ne créent pas de fermetures de l'environnement actuel. Elles sont toujours créées dans l'environnement global, elles ne sont donc accessibles qu'au moment de l'exécution. les variables et leurs propres variables locales ne peuvent pas accéder aux variables dans la portée dans laquelle elles ont été créées par le constructeur Function. Ceci est cohérent avec l'utilisation du evalFunction 构造器创建的函数不会创建当前环境的闭包,它们总是被创建于全局环境,因此在运行时它们只能访问全局变量和自己的局部变量,不能访问它们被 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 Le code qui exécute la fonction de création est différent.

    // 文件a通过module.exports导出一个变量,在文件b中使用require进行接收。
    // a.js
    module.exports = "a"
    // b.js
    let a = require('./a');
    console.log(a); // a
  • Fonction peut obtenir des variables globales, elle peut donc toujours avoir une pollution variable. Fonction est le principe d'implémentation du
  • moteur de module

    . Je publierai un article pour l'expliquer séparément à l'avenir.

  • Il existe également une solution, que nous n'avons pas développée en détail dans le dernier article, et c'est le module vm.

module vm

Dans le texte ci-dessus, j'ai mis l'accent sur un concept qui est la Pollution des variables. La caractéristique de la VM est qu'elle n'est pas affectée par l'environnement. On peut également dire qu'il s'agit d'un environnement sandbox (le mode Sandbox fournit un environnement permettant aux modules de s'exécuter sans affecter les autres modules et leurs bacs à sable privés. ).

let a = module.exports = "a";

Nous voulons souligner ici que parce que les variables globales dans

Node.js
    sont partagées sous plusieurs modules, essayez donc de ne pas définir de propriétés dans global. Les définitions de la démo sont destinées à faciliter la compréhension.
  • Supposons que nous ayons un fichier 1.js dans le même répertoire, qui définit global.a = 100;. Maintenant que nous introduisons ce fichier

    let a = (function(exports, module, require, __dirname, __filename){
      module.exports = "a";
      return module.exports
    })(...args) // exports, module, require, __dirname, __filename 将五个参数传入

    nous pouvons constater que nous ne définissons pas la variable a dans le fichier actuel, nous associons simplement les deux fichiers de module ensemble. C'est ce que j'ai mentionné ci-dessus, les variables globales dans Node sont partagées entre plusieurs modules.

    La raison est que dans l'environnement de

    Node
  • , il existe un contexte d'exécution global.
  • // a.js
    var a = 100;
    module.exports = function(){}

    Ainsi, vm.runInThisContext peut accéder aux variables globales sur global, mais ne peut pas accéder aux variables personnalisées. Cependant, vm.runInNewContext ne peut pas accéder à global, ni aux variables personnalisées. Il existe dans un tout nouveau contexte d'exécution. Et notre require est implémenté via vm.runInThisContext.

    🎜L'implémentation de require peut être divisée en quatre étapes suivantes. 🎜🎜🎜🎜Lisez les fichiers qui doivent être importés. 🎜🎜🎜🎜Après avoir lu le fichier, encapsulez le code dans une fonction. 🎜🎜🎜🎜Convertissez-le en syntaxe JS via vm.runInThisContext. 🎜🎜🎜🎜Appel à code. 🎜🎜🎜🎜Supposons que nous ayons maintenant les deux fichiers suivants. Ce sont 🎜a.js🎜 et 🎜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 将五个参数传入
    🎜 Nous pouvons analyser la logique de mise en œuvre de l'importation et de l'exportation à travers les quatre étapes ci-dessus. 🎜🎜🎜🎜🎜Lire les fichiers. 🎜🎜🎜Introduisez le contenu du fichier qui doit être importé dans le fichier qui doit être reçu, et il ressemblera à ceci🎜rrreee🎜Mais sous cette forme, Node ne peut pas l'analyser du tout, nous devons donc procéder à la deuxième étape. 🎜🎜🎜🎜🎜Encapsulez le fichier lu dans une fonction. 🎜🎜
    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 教程!!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer