日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

js ...运算符_「 giao-js 」用js写一个js解释器

發(fā)布時間:2025/3/15 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 js ...运算符_「 giao-js 」用js写一个js解释器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

在這篇文章中,我們將通過 JS 構(gòu)建我們自己的 JS 解釋器,用 JS 寫 JS,這聽起來很奇怪,盡管如此,這樣做我們將更熟悉 JS,也可以學習 JS 引擎是如何工作的!

什么是解釋器 (Interpreter) ?

解釋器是在運行時運行的語言求值器,它動態(tài)地執(zhí)行程序的源代碼。 解釋器解析源代碼,從源代碼生成 AST(抽象語法樹),遍歷 AST 并逐個計算它們。

解釋器 (Interpreter) 工作原理

  • 詞法分析 (Tokenization)
  • 語法解析 (Parsing)
  • 求值 (Evaluating)

詞法分析 (Tokenization)

將源代碼分解并組織成一組有意義的單詞,這一過程即為詞法分析(Token)。

在英語中,當我們遇到這樣一個語句時:

Javascript is the best language in the world

我們會下意識地把句子分解成一個個單詞:

+----------------------------------------------------------+ | Javascript | is | the | best | language | in |the |world | +----------------------------------------------------------+

這是分析和理解句子的第一階段。

詞法分析是由詞法分析器完成的,詞法分析器會掃描(scanning)代碼,提取詞法單元。

var a = 1;[("var": "keyword"),("a": "identifier"),("=": "assignment"),("1": "literal"),(";": "separator"), ];

詞法分析器將代碼分解成 Token 后,會將 Token 傳遞給解析器進行解析,我們來看下解析階段是如何工作的。

語法解析 (Parsing)

將詞法分析階段生成的 Token 轉(zhuǎn)換為抽象語法樹(Abstract Syntax Tree),這一過程稱之為語法解析(Parsing)。

在英語中,Javascript is the best language 被分解為以下單詞:

+------------------------------------------+ | Javascript | is | the | best | language | +------------------------------------------+

這樣我們就可以挑選單詞并形成語法結(jié)構(gòu):

"Javascript": Subject "is the best language": Predicate "language": Object

Javascript 在語法中是一個主語名詞,其余的是一個沒有什么意義的句子叫做謂語,language 是動作的接受者,也就是賓語。結(jié)構(gòu)是這樣的:

Subject(Noun) -> Predicate -> Object

語法解析是由語法解析器完成的,它會將上一步生成的 Token,根據(jù)語法規(guī)則,轉(zhuǎn)為抽象語法樹(AST)。

{type: "Program",body: [{type: "VariableDeclaration",declarations: [{type: "VariableDeclarator",id: {type: "Identifier",name: "sum"},init: {type: "Literal",value: 30,raw: "30"}}],kind: "var"}], }

求值階段 (Evaluating)

解釋器將遍歷 AST 并計算每個節(jié)點。- 求值階段1 + 2 ||v +---+ +---+ | 1 | | 2 | +---+ +---+///+---+| + |+---+ {lhs: 1,op: '+'.rhs: 2 }

解釋器解析 Ast,得到 LHS 節(jié)點,接著收集到操作符(operator)節(jié)點+,+操作符表示需要進行一次加法操作,它必須有第二個節(jié)點來進行加法操作.接著他收集到 RHS 節(jié)點。它收集到了有價值的信息并執(zhí)行加法得到了結(jié)果,3。

{type: "Program",body: [{type: "ExpressionStatement",expression: {type: "BinaryExpression",left: {type: "Literal",value: 1,raw: "1"},operator: "+",right: {type: "Literal",value: 2,raw: "2"}}}], }

實踐

前面我們已經(jīng)介紹了解釋器的工作原理,接下來我們來動動手松松筋骨吧,實現(xiàn)一個 Mini Js Interpreter~

實踐準備

  • Acorn.js
A tiny, fast JavaScript parser, written completely in JavaScript. 一個完全使用 javascript 實現(xiàn)的,小型且快速的 javascript 解析器

本次實踐我們將使用 acorn.js ,它會幫我們進行詞法分析,語法解析并轉(zhuǎn)換為抽象語法樹。

Webpack/Rollup/Babel(@babel/parser) 等第三方庫也是使用 acorn.js 作為自己 Parser 的基礎(chǔ)庫。(站在巨人的肩膀上啊!)

  • The Estree Spec

最開始 Mozilla JS Parser API 是 Mozilla 工程師在 Firefox 中創(chuàng)建的 SpiderMonkey 引擎輸出 JavaScript AST 的規(guī)范文檔,文檔所描述的格式被用作操作 JAvaScript 源代碼的通用語言。

隨著 JavaScript 的發(fā)展,更多新的語法被加入,為了幫助發(fā)展這種格式以跟上 JavaScript 語言的發(fā)展。The ESTree Spec 就誕生了,作為參與構(gòu)建和使用這些工具的人員的社區(qū)標準。

acorn.js parse 返回值符合 ESTree spec 描述的 AST 對象,這里我們使用@types/estree 做類型定義。

  • Jest

號稱令人愉快的 JavaScript 測試...我們使用它來進行單元測試.

  • Rollup

Rollup 是一個 JavaScript 模塊打包器,我們使用它來打包,以 UMD 規(guī)范對外暴露模塊。

項目初始化

// visitor.ts 創(chuàng)建一個Visitor類,并提供一個方法操作ES節(jié)點。 import * as ESTree from "estree"; class Visitor {visitNode(node: ESTree.Node) {// ...} } export default Visitor; // interpreter.ts 創(chuàng)建一個Interpreter類,用于運行ES節(jié)點樹。 // 創(chuàng)建一個Visitor實例,并使用該實例來運行ESTree節(jié)點 import Visitor from "./visitor"; import * as ESTree from "estree"; class Interpreter {private visitor: Visitor;constructor(visitor: Visitor) {this.visitor = visitor;}interpret(node: ESTree.Node) {this.visitor.visitNode(node);} } export default Interpreter; // vm.ts 對外暴露run方法,并使用acorn code->ast后,交給Interpreter實例進行解釋。 const acorn = require("acorn"); import Visitor from "./visitor"; import Interpreter from "./interpreter";const jsInterpreter = new Interpreter(new Visitor());export function run(code: string) {const root = acorn.parse(code, {ecmaVersion: 8,sourceType: "script",});return jsInterpreter.interpret(root); }

實踐第 1 彈: 1+1= ?

我們這節(jié)來實現(xiàn) 1+1 加法的解釋。首先我們通過AST explorer,看看 1+1 這段代碼轉(zhuǎn)換后的 AST 結(jié)構(gòu)。

我們可以看到這段代碼中存在 4 種節(jié)點類型,下面我們簡單的介紹一下它們:

Program

根節(jié)點,即代表一整顆抽象語法樹,body 屬性是一個數(shù)組,包含了多個 Statement 節(jié)點。

interface Program {type: "Program";sourceType: "script" | "module";body: Array<Directive | Statement | ModuleDeclaration>;comments?: Array<Comment>; }

ExpressionStatement

表達式語句節(jié)點,expression 屬性指向一個表達式節(jié)點對象

interface ExpressionStatement {type: "ExpressionStatement";expression: Expression; }

BinaryExpression

二元運算表達式節(jié)點,left 和 right 表示運算符左右的兩個表達式,operator 表示一個二元運算符。 本節(jié)實現(xiàn)的重點,簡單理解,我們只要拿到 operator 操作符的類型并實現(xiàn),然后對 left,right 值進行求值即可。

interface BinaryExpression {type: "BinaryExpression";operator: BinaryOperator;left: Expression;right: Expression; }

Literal

字面量,這里不是指 [] 或者 {} 這些,而是本身語義就代表了一個值的字面量,如 1,“hello”, true 這些,還有正則表達式,如 /d?/。

type Literal = SimpleLiteral | RegExpLiteral;interface SimpleLiteral {type: "Literal";value: string | boolean | number | null;raw?: string; }interface RegExpLiteral {type: "Literal";value?: RegExp | null;regex: {pattern: string;flags: string;};raw?: string; }

廢話少說,開擼!!!

// standard/es5.ts 實現(xiàn)以上節(jié)點方法import Scope from "../scope"; import * as ESTree from "estree"; import { AstPath } from "../types/index";const es5 = {// 根節(jié)點的處理很簡單,我們只要對它的body屬性進行遍歷,然后訪問該節(jié)點即可。Program(node: ESTree.Program) {node.body.forEach((bodyNode) => this.visitNode(bodyNode));},// 表達式語句節(jié)點的處理,同樣訪問expression 屬性即可。ExpressionStatement(node: ESTree.ExpressionStatement>) {return this.visitNode(node.expression);},// 字面量節(jié)點處理直接求值,這里對正則表達式類型進行了特殊處理,其他類型直接返回value值即可。Literal(node: ESTree.Literal>) {if ((<ESTree.RegExpLiteral>node).regex) {const { pattern, flags } = (<ESTree.RegExpLiteral>node).regex;return new RegExp(pattern, flags);} else return node.value;},// 二元運算表達式節(jié)點處理// 對left/node兩個節(jié)點(Literal)進行求值,然后實現(xiàn)operator類型運算,返回結(jié)果。BinaryExpression(node: ESTree.BinaryExpression>) {const leftNode = this.visitNode(node.left);const operator = node.operator;const rightNode = this.visitNode(node.right);return {"+": (l, r) => l + r,"-": (l, r) => l - r,"*": (l, r) => l * r,"/": (l, r) => l / r,"%": (l, r) => l % r,"<": (l, r) => l < r,">": (l, r) => l > r,"<=": (l, r) => l <= r,">=": (l, r) => l >= r,"==": (l, r) => l == r,"===": (l, r) => l === r,"!=": (l, r) => l != r,"!==": (l, r) => l !== r,}[operator](leftNode, rightNode);}, }; export default es5; // visitor.ts import Scope from "./scope"; import * as ESTree from "estree"; import es5 from "./standard/es5";const VISITOR = {...es5, }; class Visitor {// 實現(xiàn)訪問節(jié)點方法,通過節(jié)點類型訪問對應的節(jié)點方法visitNode(node: ESTree.Node) {return {visitNode: this.visitNode,...VISITOR,}[node.type](node);} } export default Visitor;

就這樣,普通的二元運算就搞定啦!!!

實踐第 2 彈: 怎么找到變量?

Javascript 的作用域與作用域鏈的概念想必大家都很熟悉了,這里就不再啰嗦了~

是的,我們需要通過實現(xiàn)作用域來訪問變量,實現(xiàn)作用域鏈來搜尋標識符。

在這之前,我們先實現(xiàn) Variable 類,實現(xiàn)變量的存取方法。

// variable.ts export enum Kind {var = "var",let = "let",const = "const", } export type KindType = "var" | "let" | "const"; export class Variable {private _value: any;constructor(public kind: Kind, val: any) {this._value = val;}get value() {return this._value;}set value(val: any) {this._value = val;} } import { Variable, Kind, KindType } from "./variable";class Scope {// 父作用域private parent: Scope | null;// 當前作用域private targetScope: { [key: string]: any };constructor(public readonly type, parent?: Scope) {this.parent = parent || null;this.targetScope = new Map();}// 是否已定義private hasDefinition(rawName: string): boolean {return Boolean(this.search(rawName));}// var類型變量定義public defineVar(rawName: string, value: any) {let scope: Scope = this;// 如果不是全局作用域且不是函數(shù)作用域,找到全局作用域,存儲變量// 這里就是我們常說的Hoisting (變量提升)while (scope.parent && scope.type !== "function") {scope = scope.parent;}// 存儲變量scope.targetScope.set(rawName, new Variable(Kind.var, value));}// let類型變量定義public defineLet(rawName: string, value: any) {this.targetScope.set(rawName, new Variable(Kind.let, value));}// const類型變量定義public defineConst(rawName: string, value: any) {this.targetScope.set(rawName, new Variable(Kind.const, value));}// 作用域鏈實現(xiàn),向上查找標識符public search(rawName: string): Variable | null {if (this.targetScope.get(rawName)) {return this.targetScope.get(rawName);} else if (this.parent) {return this.parent.search(rawName);} else {return null;}}// 變量聲明方法,變量已定義則拋出語法錯誤異常public declare(kind: Kind | KindType, rawName: string, value: any) {if (this.hasDefinition(rawName)) {console.error(`Uncaught SyntaxError: Identifier '${rawName}' has already been declared`);return true;}return {[Kind.var]: () => this.defineVar(rawName, value),[Kind.let]: () => this.defineLet(rawName, value),[Kind.const]: () => this.defineConst(rawName, value),}[kind]();} }export default Scope;

以上就是變量對象,作用域及作用域鏈的基礎(chǔ)實現(xiàn)了,接下來我們就可以定義及訪問變量了。

實踐第 3 彈: var age = 18

從語法樹中我們可以看到三個陌生的節(jié)點類型,來看看它們分別代表什么意思:

VariableDeclaration

變量聲明,kind 屬性表示是什么類型的聲明,因為 ES6 引入了 const/let。 declarations 表示聲明的多個描述,因為我們可以這樣:let a = 1, b = 2;。

interface VariableDeclaration {type: "VariableDeclaration";declarations: Array<VariableDeclarator>;kind: "var" | "let" | "const"; }

VariableDeclarator

變量聲明的描述,id 表示變量名稱節(jié)點,init 表示初始值的表達式,可以為 null。

interface VariableDeclarator {type: "VariableDeclarator";id: Pattern;init?: Expression | null; }

Identifier

顧名思義,標識符節(jié)點,我們寫 JS 時定義的變量名,函數(shù)名,屬性名,都歸為標識符。

interface Identifier {type: "Identifier";name: string; }

了解了對應節(jié)點的含義后,我們來進行實現(xiàn):

// standard/es5.ts 實現(xiàn)以上節(jié)點方法import Scope from "../scope"; import * as ESTree from "estree";type AstPath<T> = {node: T;scope: Scope; };const es5 = {// ...// 這里我們定義了astPath,新增了scope作用域參數(shù)VariableDeclaration(astPath: AstPath<ESTree.VariableDeclaration>) {const { node, scope } = astPath;const { declarations, kind } = node;// 上面提到,生聲明可能存在多個描述(let a = 1, b = 2;),所以我們這里對它進行遍歷:// 這里遍歷出來的每個item是VariableDeclarator節(jié)點declarations.forEach((declar) => {const { id, init } = <ESTree.VariableDeclarator>declar;// 變量名稱節(jié)點,這里拿到的是ageconst key = (<ESTree.Identifier>id).name;// 判斷變量是否進行了初始化 ? 查找init節(jié)點值(Literal類型直接返回值:18) : 置為undefined;const value = init ? this.visitNode(init, scope) : undefined;// 根據(jù)不同的kind(var/const/let)聲明進行定義,即var age = 18scope.declare(kind, key, value);});},// 標識符節(jié)點,我們只要通過訪問作用域,訪問該值即可。Identifier(astPath: AstPath<ESTree.Identifier>) {const { node, scope } = astPath;const name = node.name;// walk identifier// 這個例子中查找的是age變量const variable = scope.search(name);// 返回的是定義的變量對象(age)的值,即18if (variable) return variable.value;}, }; export default es5;

實踐第 4 彈: module.exports = 6

我們先來看看 module.exports = 6 對應的 AST。

從語法樹中我們又看到兩個陌生的節(jié)點類型,來看看它們分別代表什么意思:

AssignmentExpression

賦值表達式節(jié)點,operator 屬性表示一個賦值運算符,left 和 right 是賦值運算符左右的表達式。

interface AssignmentExpression {type: "AssignmentExpression";operator: AssignmentOperator;left: Pattern | MemberExpression;right: Expression; }

MemberExpression

成員表達式節(jié)點,即表示引用對象成員的語句,object 是引用對象的表達式節(jié)點,property 是表示屬性名稱,computed 如果為 false,是表示 . 來引用成員,property 應該為一個 Identifier 節(jié)點,如果 computed 屬性為 true,則是 [] 來進行引用,即 property 是一個 Expression 節(jié)點,名稱是表達式的結(jié)果值。

interface MemberExpression {type: "MemberExpression";object: Expression | Super;property: Expression;computed: boolean;optional: boolean; }

我們先來定義 module.exports 變量。

import Scope from "./scope"; import Visitor from "./visitor"; import * as ESTree from "estree"; class Interpreter {private scope: Scope;private visitor: Visitor;constructor(visitor: Visitor) {this.visitor = visitor;}interpret(node: ESTree.Node) {this.createScope();this.visitor.visitNode(node, this.scope);return this.exportResult();}createScope() {// 創(chuàng)建全局作用域this.scope = new Scope("root");// 定義module.exportsconst $exports = {};const $module = { exports: $exports };this.scope.defineConst("module", $module);this.scope.defineVar("exports", $exports);}// 模擬commonjs,對外暴露結(jié)果exportResult() {// 查找module變量const moduleExport = this.scope.search("module");// 返回module.exports值return moduleExport ? moduleExport.value.exports : null;} } export default Interpreter;

ok,下面我們來實現(xiàn)以上節(jié)點函數(shù)~

// standard/es5.ts 實現(xiàn)以上節(jié)點方法import Scope from "../scope"; import * as ESTree from "estree";type AstPath<T> = {node: T;scope: Scope; };const es5 = {// ...// 這里我們定義了astPath,新增了scope作用域參數(shù)MemberExpression(astPath: AstPath<ESTree.MemberExpression>) {const { node, scope } = astPath;const { object, property, computed } = node;// property 是表示屬性名稱,computed 如果為 false,property 應該為一個 Identifier 節(jié)點,如果 computed 屬性為 true,即 property 是一個 Expression 節(jié)點// 這里我們拿到的是exports這個key值,即屬性名稱const prop = computed? this.visitNode(property, scope): (<ESTree.Identifier>property).name;// object 表示對象,這里為module,對module進行節(jié)點訪問const obj = this.visitNode(object, scope);// 訪問module.exports值return obj[prop];},// 賦值表達式節(jié)點(astPath: AstPath<ESTree.>) {const { node, scope } = astPath;const { left, operator, right } = node;let assignVar;// LHS 處理if (left.type === "Identifier") {// 標識符類型 直接查找const value = scope.search(left.name);assignVar = value;} else if (left.type === "MemberExpression") {// 成員表達式類型,處理方式跟上面差不多,不同的是這邊需要自定義一個變量對象的實現(xiàn)const { object, property, computed } = left;const obj = this.visitNode(object, scope);const key = computed? this.visitNode(property, scope): (<ESTree.Identifier>property).name;assignVar = {get value() {return obj[key];},set value(v) {obj[key] = v;},};}// RHS// 不同操作符處理,查詢到right節(jié)點值,對left節(jié)點進行賦值。return {"=": (v) => {assignVar.value = v;return v;},"+=": (v) => {const value = assignVar.value;assignVar.value = v + value;return assignVar.value;},"-=": (v) => {const value = assignVar.value;assignVar.value = value - v;return assignVar.value;},"*=": (v) => {const value = assignVar.value;assignVar.value = v * value;return assignVar.value;},"/=": (v) => {const value = assignVar.value;assignVar.value = value / v;return assignVar.value;},"%=": (v) => {const value = assignVar.value;assignVar.value = value % v;return assignVar.value;},}[operator](this.visitNode(right, scope));}, }; export default es5;

ok,實現(xiàn)完畢,是時候驗證一波了,上 jest 大法。

// __test__/es5.test.tsimport { run } from "../src/vm"; describe("giao-js es5", () => {test("assign", () => {expect(run(`module.exports = 6;`)).toBe(6);}); }

實踐第 5 彈: for 循環(huán)

var result = 0; for (var i = 0; i < 5; i++) {result += 2; } module.exports = result;

到這一彈大家都發(fā)現(xiàn)了,不同的語法其實對應的就是不同的樹節(jié)點,我們只要實現(xiàn)對應的節(jié)點函數(shù)即可.我們先來看看這幾個陌生節(jié)點的含義.

ForStatement

for 循環(huán)語句節(jié)點,屬性 init/test/update 分別表示了 for 語句括號中的三個表達式,初始化值,循環(huán)判斷條件,每次循環(huán)執(zhí)行的變量更新語句(init 可以是變量聲明或者表達式)。 這三個屬性都可以為 null,即 for(;;){}。
body 屬性用以表示要循環(huán)執(zhí)行的語句。

interface ForStatement {type: "ForStatement";init?: VariableDeclaration | Expression | null;test?: Expression | null;update?: Expression | null;body: Statement; }

UpdateExpression

update 運算表達式節(jié)點,即 ++/--,和一元運算符類似,只是 operator 指向的節(jié)點對象類型不同,這里是 update 運算符。

interface UpdateExpression {type: "UpdateExpression";operator: UpdateOperator;argument: Expression;prefix: boolean; }

BlockStatement

塊語句節(jié)點,舉個例子:if (...) { // 這里是塊語句的內(nèi)容 },塊里邊可以包含多個其他的語句,所以有一個 body 屬性,是一個數(shù)組,表示了塊里邊的多個語句。

interface BlockStatement {0;type: "BlockStatement";body: Array<Statement>;innerComments?: Array<Comment>; }

廢話少說,盤它!!!

// standard/es5.ts 實現(xiàn)以上節(jié)點方法import Scope from "../scope"; import * as ESTree from "estree";type AstPath<T> = {node: T;scope: Scope; };const es5 = {// ...// for 循環(huán)語句節(jié)點ForStatement(astPath: AstPath<ESTree.ForStatement>) {const { node, scope } = astPath;const { init, test, update, body } = node;// 這里需要注意的是需要模擬創(chuàng)建一個塊級作用域// 前面Scope類實現(xiàn),var聲明在塊作用域中會被提升,const/let不會const forScope = new Scope("block", scope);for (// 初始化值// VariableDeclarationinit ? this.visitNode(init, forScope) : null;// 循環(huán)判斷條件(BinaryExpression)// 二元運算表達式,之前已實現(xiàn),這里不再細說test ? this.visitNode(test, forScope) : true;// 變量更新語句(UpdateExpression)update ? this.visitNode(update, forScope) : null) {// BlockStatementthis.visitNode(body, forScope);}},// update 運算表達式節(jié)點// update 運算表達式節(jié)點,即 ++/--,和一元運算符類似,只是 operator 指向的節(jié)點對象類型不同,這里是 update 運算符。UpdateExpression(astPath: AstPath<ESTree.UpdateExpression>) {const { node, scope } = astPath;// update 運算符,值為 ++ 或 --,配合 update 表達式節(jié)點的 prefix 屬性來表示前后。const { prefix, argument, operator } = node;let updateVar;// 這里需要考慮參數(shù)類型還有一種情況是成員表達式節(jié)點// 例: for (var query={count:0}; query.count < 8; query.count++)// LHS查找if (argument.type === "Identifier") {// 標識符類型 直接查找const value = scope.search(argument.name);updateVar = value;} else if (argument.type === "MemberExpression") {// 成員表達式的實現(xiàn)在前面實現(xiàn)過,這里不再細說,一樣的套路~const { object, property, computed } = argument;const obj = this.visitNode(object, scope);const key = computed? this.visitNode(property, scope): (<ESTree.Identifier>property).name;updateVar = {get value() {return obj[key];},set value(v) {obj[key] = v;},};}return {"++": (v) => {const result = v.value;v.value = result + 1;// preifx? ++i: i++;return prefix ? v.value : result;},"--": (v) => {const result = v.value;v.value = result - 1;// preifx? --i: i--;return prefix ? v.value : result;},}[operator](updateVar);},// 塊語句節(jié)點// 塊語句的實現(xiàn)很簡單,模擬創(chuàng)建一個塊作用域,然后遍歷body屬性進行訪問即可。BlockStatement(astPath: AstPath<ESTree.BlockStatement>) {const { node, scope } = astPath;const blockScope = new Scope("block", scope);const { body } = node;body.forEach((bodyNode) => {this.visitNode(bodyNode, blockScope);});}, }; export default es5;

上 jest 大法驗證一哈~

test("test for loop", () => {expect(run(`var result = 0;for (var i = 0; i < 5; i++) {result += 2;}module.exports = result;`)).toBe(10); });

你以為這樣就結(jié)束了嗎? 有沒有想到還有什么情況沒處理? for 循環(huán)的中斷語句呢?

var result = 0; for (var i = 0; i < 5; i++) {result += 2;break; // break,continue,return } module.exports = result;

感興趣的小伙伴可以自己動手試試,或者戳源碼地址

結(jié)語

giao-js目前只實現(xiàn)了幾個語法,本文只是提供一個思路。

有興趣的同學可以查看完整代碼。

覺得有幫助到你的話,點個 star 支持下作者 ?? ~

參考

bramblex/jsjs

使用 Acorn 來解析 JavaScript

Build a JS Interpreter in JavaScript Using Acorn as a Parser

總結(jié)

以上是生活随笔為你收集整理的js ...运算符_「 giao-js 」用js写一个js解释器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。