>  기사  >  웹 프론트엔드  >  JavaScript를 사용하여 js 인터프리터 작성

JavaScript를 사용하여 js 인터프리터 작성

hzc
hzc앞으로
2020-07-02 09:33:022966검색

js를 사용하여 js를 컴파일하는 것은 고급스러운 것처럼 보이지만 실제로는 js의 기능을 사용하는 것보다 훨씬 간단합니다. 객체 속성을 문자열로 표현할 수 있습니다. 그냥 흑마법이죠. 编译 js 看起来是个高大上的东西,实际原理其实很简单,无非就是利用 js 对象属性可以用字符串表示 这个特性来实现的黑魔法罢了。
之所以看起来那么 深奥, 大概是由于网上现有的教程,都是动不动就先来个 babylon / @babel/parser 先让大家看个一大串的 AST, 然后再贴出一大串的代码,
直接递归 AST 处理所有类型的节点. 最后新手就成功被吓跑了。

那么今天我写这篇的目的,就是给大家一个浅显易懂,连刚学 js 的人都能看懂的 js2js 教程。

先来看一下效果

JavaScript를 사용하여 js 인터프리터 작성

一个最简单的解释器

上面有提到,js 有个特性是 对象属性可以用字符串表示,如 console.log 等价于 console['log'], 辣么根据这个特性,我们可以写出一个兼容性极差,极其简陋的雏形

  function callFunction(fun, arg) {

    this[fun](arg);

  }

  callFunction('alert', 'hello world');

  // 如果你是在浏览器环境的话,应该会弹出一个弹窗

既然是简易版的,肯定是问题一大堆,js 里面得语法不仅仅是函数调用,我们看看赋值是如何用黑魔法实现的

  function declareVarible(key, value) {

    this[key] = value;

  }

  declareVarible.call(window, 'foo', 'bar');

  // window.foo = 'bar'

Tips: const 可以利用 Object.defineProperty 实现;

如果上面的代码能看懂,说明你已经懂得了 js 解释器 的基本原理了,看不懂那只好怪我咯。

稍微加强一下

可以看出,上面为了方便, 我们把函数调用写成了 callFunction('alert', 'hello world'); 但是着看起来一点都不像是 js 解释器,
我们心里想要的解释器至少应该是长这样的 parse('alert("hello world")''), 那么我们来稍微改造一下, 在这里我们要引入 babel 了,
不过先不用担心, 我们解析出来的语法树(AST)也是很简单的。

import babelParser from '@babel/parser';

const code = 'alert("hello world!")';

const ast = babelParser.parse(code);

以上代码, 解析出如下内容

{
  "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"
}

上面的内容看起来很多,但是我们实际有用到到其实只是很小的一部分, 来稍微简化一下, 把暂时用不到的字段先去掉

{
  "type": "Program",
  "body": [
    {
      "type": "ExpressionStatement",
      "expression": {
        "type": "CallExpression",
        "callee": {
          "type": "Identifier",
          "name": "alert"
        },
        "arguments": [
          {
            "type": "Literal",
            "value": "hello world!",
          }
        ]
      }
    }
  ],
}

我们先大概浏览一遍 AST 里面的所有属性名为 type 的数据

  1. ExpressionStatement
  2. CallExpression
  3. Identifier
  4. Literal

一共有 4 种类型, 那么接下来我们把这 4 种节点分别解析, 从最简单的开始

Literal

{
    "type": "Literal",
    "value": "hello world!",
}

针对 Literal 的内容, 我们需要的只有一个 value 属性, 直接返回即可.

if(node.type === 'Literal') {
    return node.value;
}

是不是很简单?

Identifier

{
    "type": "Identifier",
    "name": "alert"
},

Identifier 同样也很简单, 它代表的就是我们已经存在的一个变量, 变量名是node.name, 既然是已经存在的变量, 那么它的值是什么呢?

if(node.type === 'Identifier') {
    return {
      name: node.name,
      value:this[node.name]
    };
}

上面的 alert 我们从 node.name 里面拿到的是一个字符, 通过 this['xxxxx'] 可以访问到当前作用域(这里是 window)里面的这个标识符(Identifier)

ExpressionStatement

{
    "type": "ExpressionStatement",
    "expression": {...}
}

这个其实也是超简单, 没有什么实质性的内容, 真正的内容都在 expression이렇게 심오하게 보이는 이유는 아마도 인터넷의 기존 튜토리얼이 항상 babylon / @babel/parser로 시작하기 때문일 것입니다. AST 목록, 그리고 긴 코드 목록,

직접 재귀 AST를 사용하여 모든 유형의 노드를 처리하는 데 성공했습니다.

그래서 오늘 이 글을 쓰는 목적은 js를 이제 막 배운 사람들도 이해하기 쉽고 이해할 수 있는 js2js 튜토리얼을 제공하는 것입니다.

먼저 효과를 살펴보겠습니다

42ad75860431f8570438749ed 31af8f.png
  1. 가장 간단한 인터프리터
  2. 위에서 언급했듯이 js에는 객체 속성을 문자열로 표현할 수 있는 기능이 있습니다. 예를 들어 console.log는 console[' log'] 이 기능을 기반으로 하면 매우 형편없고 투박한 프로토타입을 작성할 수 있습니다
  3. if(node.type === 'ExpressionStatement') {
        return parseAstNode(node.expression);
    }
간단한 버전이므로 많은 문제가 있을 것입니다. js의 구문은 단지 함수 호출이 아닙니다. 흑마술을 사용해 구현됩니다

{
    "type": "CallExpression",
    "callee": {...},
    "arguments": [...]
}
Tips: const는 Object.defineProperty를 사용하여 구현할 수 있습니다.

위 코드를 이해할 수 있다면 js 설명을 이미 이해했다는 뜻입니다. 기기의 원리를 이해하지 못한다면 제 잘못입니다.

약간 강화하세요

위에서는 편의상 함수 호출을 callFunction('alert', 'hello world');로 작성했지만 실제로는 그렇지 않은 것을 볼 수 있습니다. alljs 인터프리터,

우리가 원하는 인터프리터는 최소한 parse('alert("hello world")'')와 같아야 합니다. 그러면 약간 수정해 보겠습니다. , 여기서는 바벨을 소개할 예정이지만, 아직 걱정하지 마세요. 우리가 파싱한 구문 트리(AST)도 매우 간단합니다.

if(node.type === 'CallExpression') {

    // 函数
    const callee = 调用 Identifier 处理器

    // 参数
    const args = node.arguments.map(arg => {
      return 调用 Literal 处理器
    });

    callee(...args);
}

위 코드는 다음 내용을 파싱합니다

  const script = document.createElement("script");
  script.innerText = 'alert("hello world!")';
  document.body.appendChild(script);
    위 내용은 많은 것 같지만 실제로 우리가 사용하는 것은 아주 작은 부분에 불과합니다. 조금 단순화해서 일시적으로 사용되지 않는 필드를 제거해 보겠습니다.
  1. 먼저 type 속성 이름을 사용하여 AST의 모든 데이터를 간략하게 살펴보겠습니다.
    ExpressionStatement
  1. CallExpression
    Identifier
  1. Literal
  1. 총 4가지 유형이 있는데, 다음과 같이 입력해 보겠습니다. this 네 가지 유형의 노드는 가장 간단한 것부터 시작하여 별도로 구문 분석됩니다.
Literal

eval('alert("hello world!")')

Literal의 내용에 필요한 것은 직접 반환할 수 있는 값 속성뿐입니다.
new Function('alert("hello world")')();
매우 간단하지 않나요? ?
Identifier

setTimeout('console.log("hello world")');
Identifier도 매우 간단합니다. 이미 존재하는 변수를 나타냅니다. 변수 이름은 기존 변수이므로 그 값은 무엇입니까?

rrreee

node.name에서 받은 위의 alert는 문자이며 this['xxxxx']를 통해 액세스할 수 있습니다. 식별자 (식별자)🎜🎜ExpressionStatement🎜rrreee🎜(여기서는 창)는 실제로 매우 간단하며 실제 내용은 expression 속성에 있으므로 직접 반환할 수 있습니다. content ofexpression🎜rrreee🎜CallExpression🎜🎜CallExpression은 문자 그대로 함수 호출 표현식을 의미하는데, 이는 좀 더 번거롭습니다.🎜rrreee🎜CallExpression 여기에 필요한 필드가 2개 있습니다:🎜🎜🎜callee 예 함수 참조, 내부 내용은 식별자입니다. 🎜🎜인수 내부 내용은 호출 시 전달되는 매개변수 배열입니다. 현재 처리해야 할 것은 리터럴이며, 위에 처리 방법이 이미 있습니다. 어떻게 하는지 이미 알고 계시리라 믿으세요🎜rrreee🎜Code🎜🎜위의 프로세스를 실행할 수 있는 간단한 구현이 있지만 위의 프로세스만 실행할 수 있으며 다른 기능은 아직 구현되지 않았습니다. 🎜🎜https://github.com/noahlam/pr...🎜🎜기타 구현 방법🎜🎜위에 소개한 가장 번거로운 방법 외에도 실제로 js Way에서 문자열 코드를 직접 실행하는 방법은 여러 가지가 있습니다🎜🎜🎜 스크립트 삽입 DOM🎜🎜rrreee🎜🎜eval🎜🎜rrreee🎜🎜new Function🎜🎜rrreee🎜🎜setTimeout family🎜🎜rrreee🎜하지만 미니 프로그램에서는 가차없이 차단됩니다...🎜마지막으로, 🎜프론트엔드 학습을 권장합니다 고급 내부 커뮤니케이션 그룹 685910553🎜 (프론트엔드 데이터 공유) 지구상 어디에 있든, 🎜몇 년 동안 일했든 상관없이 가입하실 수 있습니다! (그룹에서는 정기적으로 그룹 오너가 수집한 학습서 및 자료와 인터뷰 질문 및 답변 문서를 무료로 제공합니다!) 🎜🎜이 기사에 이의가 있는 경우 기사 댓글에 의견을 적어주세요. 🎜🎜이 기사가 흥미로웠다면 공유하고 전달해 주세요. 또는 팔로우하여 기사에 대한 인식과 격려를 표시할 수도 있습니다. 🎜

모두가 프로그래밍의 길에서 점점 더 멀리 나아갈 수 있기를 바랍니다.

추천 튜토리얼: "JS Tutorial"

위 내용은 JavaScript를 사용하여 js 인터프리터 작성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 jianshu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제
이전 기사:정규식 기본 사항다음 기사:정규식 기본 사항