Maison >interface Web >js tutoriel >Écrire un interpréteur js en utilisant JavaScript
Utiliser js pour 编译 js
semble être une chose haut de gamme, mais le principe réel est en fait très simple. Ce n'est rien de plus que de la magie noire réalisée en utilisant la fonctionnalité de js 对象属性可以用字符串表示
. La raison pour laquelle
a cette apparence 深奥
est probablement parce que les tutoriels existants sur Internet vous donnent toujours un babylon / @babel/parser
d'abord, laissez tout le monde voir une grande liste de AST
, puis publiez une grande liste d'entre eux. ,
AST directement récursif pour traiter tous les types de nœuds Au final, les novices ont réussi à faire peur.
Le but de la rédaction de cet article aujourd'hui est de vous proposer un tutoriel js2js facile à comprendre et pouvant être compris même par ceux qui viennent d'apprendre js.
Jetons d'abord un coup d'œil à l'effet
Comme mentionné ci-dessus, js a une fonctionnalité qui est对象属性可以用字符串表示
, par exemple, console.log est équivalent à console['log'], donc sur la base de cette fonctionnalité, nous pouvons écrire un prototype extrêmement pauvre et grossier
function callFunction(fun, arg) { this[fun](arg); } callFunction('alert', 'hello world'); // 如果你是在浏览器环境的话,应该会弹出一个弹窗
Comme il s'agit d'une version simplifiée, elle doit être Il y a beaucoup de problèmes. La syntaxe en js ne concerne pas seulement les appels de fonction. Voyons comment l'affectation est implémentée à l'aide de la magie noire
function declareVarible(key, value) { this[key] = value; } declareVarible.call(window, 'foo', 'bar'); // window.foo = 'bar'
Const : const peut être implémenté en utilisant Object.defineProperty;
Si vous pouvez comprendre le code ci-dessus, cela signifie que vous comprenez déjà les principes de base de js 解释器
. Si vous ne le comprenez pas, vous ne pouvez que m'en vouloir.
Comme vous pouvez le voir, pour plus de commodité ci-dessus, nous avons écrit l'appel de fonction comme callFunction('alert', 'hello world');
, mais il ne ressemble pas du tout à js 解释器
,
dans notre esprit L'interpréteur souhaité devrait au moins ressembler à ceci parse('alert("hello world")'')
, faisons donc une petite modification Ici, nous devons introduire babel,
mais ne vous inquiétez pas pour l'instant, l'arbre syntaxique (AST) que nous avons analysé est également très. simple.
import babelParser from '@babel/parser'; const code = 'alert("hello world!")'; const ast = babelParser.parse(code);
Le code ci-dessus analyse le contenu suivant
{ "type": "Program", "start": 0, "end": 21, "body": [ { "type": "ExpressionStatement", "start": 0, "end": 21, "expression": { "type": "CallExpression", "start": 0, "end": 21, "callee": { "type": "Identifier", "start": 0, "end": 5, "name": "alert" }, "arguments": [ { "type": "Literal", "start": 6, "end": 20, "value": "hello world!", "raw": "\"hello world!\"" } ] } } ], "sourceType": "module" }
Le contenu ci-dessus semble être important, mais ce que nous utilisons réellement n'est en fait qu'une petite partie. Simplifions-le un peu et économisons. les choses qui ne sont pas nécessaires pour le moment. Supprimons les champs qui arrivent en premier
{ "type": "Program", "body": [ { "type": "ExpressionStatement", "expression": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "alert" }, "arguments": [ { "type": "Literal", "value": "hello world!", } ] } } ], }
Parcourons d'abord toutes les données avec les noms d'attribut type
dans l'AST
Il existe 4 types au total, nous analyserons ensuite ces 4 types de nœuds séparément, en commençant par le plus simple
{ "type": "Literal", "value": "hello world!", }
Pour le contenu littéral, tout ce dont nous avons besoin est un attribut de valeur, qui peut être renvoyé directement.
if(node.type === 'Literal') { return node.value; }
N'est-ce pas très simple ?
{ "type": "Identifier", "name": "alert" },
L'identifiant est également très simple. Il représente une variable que nous avons déjà. Le nom de la variable est node.name. Puisqu'il s'agit d'une variable existante, sa valeur est Quoi ?
if(node.type === 'Identifier') { return { name: node.name, value:this[node.name] }; }
Le alert
ci-dessus que nous obtenons de node.name
est un personnage, via this['xxxxx']
nous pouvons accéder à l'identifiant (Identifier)
{ "type": "ExpressionStatement", "expression": {...} }
, vous pouvez donc renvoyer directement le contenu de l'expression expression
if(node.type === 'ExpressionStatement') { return parseAstNode(node.expression); }CallExpressionCallExpression signifie littéralement une expression d'appel de fonction, ce qui est un peu plus gênant
{ "type": "CallExpression", "callee": {...}, "arguments": [...] }CallExpression a 2 champs dont nous avons besoin :
if(node.type === 'CallExpression') { // 函数 const callee = 调用 Identifier 处理器 // 参数 const args = node.arguments.map(arg => { return 调用 Literal 处理器 }); callee(...args); }CodeVoici une implémentation simple qui peut être exécuté Le processus ci-dessus ne peut être exécuté que et d'autres fonctionnalités n'ont pas encore été implémentées. https://github.com/noahlam/pr...Autres méthodes d'implémentationEn plus de la méthode la plus lourde que j'ai présentée ci-dessus, en fait, js Il existe plusieurs façons d'exécuter directement du code de chaîne
const script = document.createElement("script"); script.innerText = 'alert("hello world!")'; document.body.appendChild(script);
eval('alert("hello world!")')
new Function('alert("hello world")')();
setTimeout('console.log("hello world")');
Enfin, donnez Nous recommandons un
groupe de communication interne avancé d'apprentissage front-end 685910553 (partage d'informations front-end), peu importe où vous êtes sur terre, peu importe depuis combien d'années vous travaillez, vous êtes invités à nous rejoindre ! (Le groupe fournira régulièrement des livres d'étude gratuits et du matériel collecté par le propriétaire du groupe ainsi que des questions d'entretien et des documents de réponses compilés !)
J'espère que chacun pourra aller de plus en plus loin sur le chemin de la programmation.
Tutoriel recommandé : "Tutoriel JS"
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!