Skip to content

leyen-me/tinyjs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🚀 TinyJS - 轻量级 JavaScript 解释器

一个功能完善的 JavaScript 解释器实现,用于深入理解编程语言的词法分析、语法解析和运行时执行原理。

🧠 本项目不仅实现了 JavaScript 的核心特性,还包括 ES6+ 的现代语法支持,如箭头函数、解构赋值、类、Promise 等高级功能。


✨ 核心特性

🔤 数据类型

  • 基本类型:Number、String、Boolean、null、undefined、NaN、Infinity
  • 复合类型:Object、Array、Function、Class
  • ES6 集合:Set、Map(完整实现,支持 add、delete、has、clear、forEach 等方法)

📝 变量声明

  • let:块级作用域变量
  • const:常量声明,不可重新赋值
  • var:函数作用域变量
  • 暂时性死区(TDZ):正确处理变量声明前访问
  • 块级作用域:支持 if、for、while 等语句的块级作用域

🧮 运算符

  • 算术运算符+-*/%
  • 比较运算符=====!=!==<><=>=
  • 逻辑运算符&&||!
  • 位运算符&|^~<<>>>>>
  • 赋值运算符=+=-=*=/=
  • 一元运算符++--+-!typeofvoiddelete
  • 三元运算符? :
  • 逗号运算符,
  • 成员运算符ininstanceof
  • 展开运算符...(在数组、函数参数中)

🎯 控制流

  • 条件语句:if/else、switch/case/default
  • 循环语句:for、while、do-while、for...in、for...of
  • 跳转语句:break、continue、return

🔧 函数

  • 函数声明function name() {}
  • 函数表达式const fn = function() {}
  • 箭头函数(param) => { return value; }
  • 默认参数function fn(a = 1, b = 2) {}
  • 剩余参数function fn(...args) {}
  • 闭包:完整的词法作用域支持
  • 高阶函数:函数作为参数和返回值
  • 递归优化:尾递归检测和优化

🏗️ 面向对象

  • 类声明class ClassName {}
  • 构造函数constructor() {}
  • 实例方法methodName() {}
  • 静态方法static methodName() {}
  • Getter/Setterget prop() {} / set prop(value) {}
  • 类继承class Child extends Parent {}
  • super 调用super()super.method()
  • instanceof 检测:完整的原型链支持

🎁 解构赋值

  • 数组解构
    • 基本解构:let [a, b, c] = [1, 2, 3]
    • 默认值:let [a = 1, b = 2] = []
    • 嵌套解构:let [a, [b, c]] = [1, [2, 3]]
    • 剩余模式:let [a, ...rest] = [1, 2, 3, 4]
    • 跳过元素:let [a, , b] = [1, 2, 3]
  • 对象解构
    • 基本解构:let {a, b} = {a: 1, b: 2}
    • 重命名:let {a: x, b: y} = {a: 1, b: 2}
    • 默认值:let {a = 1, b = 2} = {}
    • 嵌套解构:let {a, b: {c, d}} = {a: 1, b: {c: 2, d: 3}}
    • 剩余属性:let {a, ...rest} = {a: 1, b: 2, c: 3}

⚡ 异步编程

  • Promise:完整实现
    • new Promise((resolve, reject) => {})
    • promise.then(onFulfilled, onRejected)
    • promise.catch(onRejected)
    • promise.finally(onFinally)
    • Promise.resolve(value)
    • Promise.reject(reason)
    • Promise.all(iterable)
    • Promise.race(iterable)

🛡️ 错误处理

  • 异常捕获:try/catch/finally 完整支持
  • 错误类型:ReferenceError、TypeError、RangeError、SyntaxError
  • 自定义错误throw new Error(message)
  • 错误传播:正确的错误冒泡机制

🔍 高级特性

  • 作用域链:正确的词法作用域和作用域链查找
  • 变量提升:模拟 JavaScript 的变量提升行为
  • 闭包机制:捕获外部变量,支持多层嵌套
  • 原型继承:完整的原型链支持
  • this 绑定:正确处理方法调用中的 this
  • 内置对象:Math、JSON、Array、String、Object 等
  • 沙箱限制:调用栈深度限制、执行时间限制、循环次数限制

📦 测试覆盖

项目包含 14 个测试套件,超过 400+ 测试用例,全面验证功能正确性:

基础功能测试

  • variables.test.js (83 行) - 变量声明、类型、赋值操作符
  • constants.test.js - const 声明和不可变性
  • operators.test.js (480 行) - 一元、位运算、逻辑、比较运算符
  • comments.test.js - 单行、多行注释处理

控制流测试

  • control-flow.test.js - if/else、switch、三元运算符
  • scope.test.js - 作用域链、块级作用域、闭包

函数与高级特性测试

  • functions.test.js (54 行) - 函数声明、表达式、高阶函数
  • advanced-features.test.js (429 行) - 闭包、箭头函数、默认参数、剩余参数

面向对象测试

  • class.test.js (401 行) - 类、继承、静态方法、getter/setter

数据结构测试

  • data-structures.test.js - 数组、对象字面量、属性访问
  • collections.test.js (536 行) - Set 和 Map 完整实现
  • destructuring.test.js (277 行) - 数组和对象解构赋值

异步与错误处理测试

  • promise.test.js (299 行) - Promise 完整实现
  • exceptions.test.js - try/catch/finally、错误类型

🎮 在线体验

🌐 访问演练场

在浏览器中打开项目根目录的 index.html 即可访问 TinyJS 网站:

  • 🏠 宣传页index.html - 项目介绍和特性展示
  • 🎯 演练场playground.html - 交互式代码测试平台

演练场功能:

  • 96+ 代码示例,涵盖所有语法特性(从测试用例自动同步)
  • 7 大分类、23 个子分类,系统化学习路径
  • ✅ Monaco 编辑器,VS Code 同款体验
  • ✅ 实时运行代码,查看结果和控制台输出
  • ✅ 浅色/暗色主题切换
  • ✅ 代码搜索和分类浏览
  • ✅ 快捷键支持(Cmd/Ctrl + Enter 运行)
  • 示例与测试同步,保证代码正确性

🔧 使用方式

安装与运行

# 克隆仓库
git clone https://github.com/leyen-me/tinyjs.git
cd tinyjs

# 安装依赖
npm install
#
pnpm install

# 运行测试
npm test

# 运行特定测试文件
npm test -- class.test.js

# 查看测试覆盖率
npm run test:coverage

# 打开网站(使用任意 HTTP 服务器)
python3 -m http.server 8000
# 然后访问 http://localhost:8000

代码示例

基本使用

import { run } from './src/main.js';

// 基本运算
const result1 = run(`
  let a = 10;
  let b = 20;
  a + b
`);
console.log(result1); // 30

// 函数和闭包
const result2 = run(`
  function createCounter() {
    let count = 0;
    return function() {
      count = count + 1;
      return count;
    };
  }
  
  const counter = createCounter();
  counter(); // 1
  counter(); // 2
  counter()  // 3
`);
console.log(result2); // 3

类和继承

run(`
  class Animal {
    constructor(name) {
      this.name = name;
    }
    
    speak() {
      return this.name + " makes a sound";
    }
  }
  
  class Dog extends Animal {
    constructor(name, breed) {
      super(name);
      this.breed = breed;
    }
    
    speak() {
      return this.name + " barks";
    }
  }
  
  const dog = new Dog("Buddy", "Labrador");
  console.log(dog.speak()); // "Buddy barks"
`);

Promise 异步编程

run(`
  let result;
  Promise.resolve(10)
    .then(x => x * 2)
    .then(x => x + 5)
    .then(x => { result = x; });
  
  console.log(result); // 25
`);

解构赋值

run(`
  // 数组解构
  let [a, b, ...rest] = [1, 2, 3, 4, 5];
  console.log(a, b, rest); // 1, 2, [3, 4, 5]
  
  // 对象解构
  let {name, age, ...others} = {name: "Alice", age: 25, city: "NY", job: "Engineer"};
  console.log(name, age, others); // "Alice", 25, {city: "NY", job: "Engineer"}
`);

Set 和 Map 集合

run(`
  // Set 集合
  let s = new Set([1, 2, 3, 2, 1]);
  s.add(4);
  console.log(s.size); // 4
  console.log(s.has(2)); // true
  
  // Map 映射
  let m = new Map();
  m.set("key1", "value1");
  m.set("key2", "value2");
  console.log(m.get("key1")); // "value1"
  console.log(m.size); // 2
`);

🔄 Playground 示例同步机制

为了确保 Playground 中展示的示例代码始终正确且与测试用例保持同步,我们建立了一套自动化的同步机制:

工作原理

测试用例文件 (test/*.test.js)
    ↓
提取脚本 (scripts/extract-examples.js)
    ↓
示例数据 (assets/js/examples.js)
    ↓
Playground UI (playground.html)

核心优势

  • 质量保证:所有示例均基于通过的测试用例
  • 自动同步:测试更新后可自动重新生成示例
  • 全面覆盖:从 293 个测试用例中精选 96 个代表性示例
  • 分类清晰:7 大类 23 个子类,系统化组织
  • 可追溯性:每个示例都关联到源测试用例

示例分类体系

一级分类 二级分类数 示例数 覆盖内容
基础语法 3 17 变量、常量、运算符
控制流 4 14 条件、循环、作用域
函数 3 7 声明、箭头、参数
面向对象 3 9 类、继承、访问器
数据结构 5 25 数组、对象、集合、解构
异步与错误 2 10 Promise、异常处理
高级特性 3 7 闭包、高阶、柯里化
总计 23 96 完整语法覆盖

使用方法

# 重新生成 Playground 示例
node scripts/extract-examples.js

# 输出示例:
# ✅ examples.js 已生成
# 📊 统计信息:
#    - 一级分类: 7 个
#    - 二级分类: 23 个
#    - 示例总数: 96 个
#    - 覆盖率提升: 从 38 个增加到 96 个 (153% 增长)

详细说明请参考 EXAMPLES_SYNC.md 文档。


📚 技术实现

核心模块

  1. 词法分析器(Lexer)

    • 正则表达式 tokenization
    • 关键字、操作符、标识符识别
    • 注释过滤(单行 // 和多行 /* */
  2. 语法分析器(Parser)

    • 递归下降分析(Recursive Descent Parsing)
    • 抽象语法树(AST)构建
    • 运算符优先级处理
    • 语句与表达式解析
  3. 执行引擎(Evaluator)

    • AST 遍历执行
    • 环境对象管理(Environment)
    • 作用域链实现
    • 闭包支持
  4. 作用域管理

    • Environment 基类:基础环境
    • BlockEnvironment:块级作用域
    • FunctionEnvironment:函数作用域
    • 变量查找缓存优化
  5. 错误处理

    • 自定义错误类型
    • 异常传播机制
    • 调用栈管理
  6. 性能优化

    • 尾递归优化(TCO)
    • 调用栈深度限制
    • 循环计数器
    • 执行时间监控

支持的语法结构

// AST 节点类型(部分)
- Literal              // 字面量
- Identifier           // 标识符
- BinaryExpression     // 二元表达式
- UnaryExpression      // 一元表达式
- AssignmentExpression // 赋值表达式
- UpdateExpression     // 更新表达式 (++/--)
- CallExpression       // 函数调用
- MemberExpression     // 成员访问
- ArrayExpression      // 数组字面量
- ObjectExpression     // 对象字面量
- FunctionExpression   // 函数表达式
- ArrowFunctionExpression // 箭头函数
- ClassExpression      // 类表达式
- ConditionalExpression // 三元表达式
- SequenceExpression   // 逗号表达式
- ArrayPattern         // 数组解构模式
- ObjectPattern        // 对象解构模式
- RestElement          // 剩余元素
- SpreadElement        // 展开元素

🎯 学习价值

本项目适合作为以下学习场景的参考:

📝 编译原理实践

  • 词法分析(Tokenization)实现
  • 语法分析(Parsing)策略
  • AST 设计与遍历
  • 运算符优先级处理

⚙️ 解释器设计模式

  • 环境对象模式(Environment Pattern)
  • 访问者模式(Visitor Pattern)
  • 作用域链实现
  • 闭包机制

🛠️ JavaScript 运行机制

  • 变量提升(Hoisting)
  • 执行上下文(Execution Context)
  • 原型链继承
  • this 绑定机制

🔍 ES6+ 特性理解

  • 箭头函数实现
  • 解构赋值算法
  • Promise 异步机制
  • Class 类语法糖

📈 性能与限制

沙箱配置

// 默认限制
const SANDBOX_LIMITS = {
  maxCallStackDepth: 1000,      // 最大调用栈深度
  maxExecutionTime: 5000,       // 最大执行时间(5秒)
  maxLoopIterations: 1000000    // 最大循环次数
};

优化特性

  • ✅ 尾递归优化(Tail Call Optimization)
  • ✅ 变量查找缓存
  • ✅ 调用栈管理
  • ✅ 循环监控

已知限制

  • ⚠️ 不支持实际的异步执行(Promise 是同步模拟)
  • ⚠️ 没有实现 async/await 语法
  • ⚠️ 不支持 ES Modules 的 import/export
  • ⚠️ 没有实现所有内置 API(仅支持常用的)

📊 项目统计

代码统计:
- src/main.js:        ~3500 行
- 测试文件:          14 个
- 测试用例:          400+ 个
- 测试代码:          ~3000 行

功能覆盖:
- 基础语法:          ✅ 100%
- 控制流:            ✅ 100%
- 函数特性:          ✅ 100%
- 面向对象:          ✅ 100%
- ES6+ 特性:        ✅ 90%
- 异步编程:          ✅ 80% (同步模拟)

🤝 贡献指南

欢迎提交 issue 和 pull request!

贡献方向

  • ✨ 添加更多 JavaScript 特性(如 async/await、生成器等)
  • ⚡ 性能优化(词法分析、执行引擎)
  • 🐛 修复 bug 和边界情况
  • 📝 完善文档和注释
  • ✅ 增加测试用例

开发流程

# 1. Fork 并克隆项目
git clone https://github.com/leyen-me/tinyjs.git

# 2. 创建功能分支
git checkout -b feature/your-feature-name

# 3. 开发并测试
npm test

# 4. 提交代码
git commit -m "feat: add your feature"

# 5. 推送分支
git push origin feature/your-feature-name

# 6. 提交 Pull Request

代码规范

  • 遵循现有代码风格
  • 添加必要的注释(尤其是复杂逻辑)
  • 每个新功能必须有对应测试用例
  • 确保所有测试通过

📖 相关资源

学习资料

相关项目


❓ 常见问题

Q: 为什么 Promise 是同步执行的?

A: 本项目是一个同步解释器,没有实现事件循环和异步队列。Promise 只是模拟了 API,实际上是立即执行的。

Q: 为什么有些 JavaScript 特性不支持?

A: 本项目的目标是教学和学习,不是完整的 JavaScript 引擎。我们优先实现最核心的特性以保持代码的可读性。

Q: 如何调试解释器?

A: 可以在 src/main.js 中添加 console.log,或者使用 Node.js 调试器:

node --inspect-brk node_modules/.bin/jest --runInBand

Q: 性能如何?

A: 作为教学项目,性能不是首要目标。但我们实现了尾递归优化和变量缓存等优化手段。


📄 许可证

MIT License. 详见 LICENSE 文件。


💬 联系方式

如有问题或建议,欢迎通过以上方式联系或提交 GitHub Issue!


⭐ Star History

如果这个项目对你有帮助,请给个 Star ⭐ 支持一下!


🚀 Made with ❤️ by TinyJS Team

首页 · 文档 · 示例 · 贡献指南

About

tiny javascript

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published