手搓JSVM第 10 篇:对象、数组与属性访问

1. 本文目标

本篇继续扩展 VM 的运行时能力,让它支持对象和数组:

plaintext 复制代码
let obj = { x: 1 };
obj.x;
​
let arr = [1, 2];
arr[0];

为此需要新增以下 opcode:

  1. OBJECT_NEW

  2. OBJECT_SET

  3. ARRAY_NEW

  4. ARRAY_PUSH

  5. GET_PROP

  6. SET_PROP

  7. NEW

2. 为什么对象需要运行时能力

数字和字符串可以直接作为值处理,而对象更像一个需要在运行时维护属性的容器。

因此,遇到对象属性访问:

plaintext 复制代码
obj.x

它并不是简单读取变量 x,而是要在运行时完成三个步骤:

plaintext 复制代码
先得到 obj
再得到属性名 'x'
再执行 obj['x']

形象化比喻:文件柜和抽屉标签

对象像文件柜,属性名像抽屉标签:

plaintext 复制代码
obj 文件柜
  x 抽屉 -> 1
  y 抽屉 -> 2

obj.x 就是去 obj 文件柜里按 x 这个标签找到对应抽屉,再取出里面的值。

3. 前置知识

概念 说明
Object 属性键值表
Property Key 字符串或 symbol,这里先简化为字符串
MemberExpression obj.xobj[x]
Array 有序元素集合
Constructor 可被 new 调用的函数

4. 源码中的对应位置

概念 源码位置 说明
GET_PROP / SET_PROP src/runtime/opcodes.ts 属性读写 opcode
OBJECT_NEW / OBJECT_SET src/runtime/opcodes.ts 对象字面量
ARRAY_NEW / ARRAY_PUSH src/runtime/opcodes.ts 数组字面量
NEW src/runtime/opcodes.ts 构造调用
runtime cases src/compiler/runtime-gen.ts 实际执行属性读写和构造
lowering object/array/member src/compiler/lowering.ts AST 到 IR

5. 核心数据结构

Object Value

它解决什么问题

它负责保存对象的属性键值对,后续的属性读写都落到这个容器上。

教学版简化实现

plaintext 复制代码
const obj = {};
obj['x'] = 1;

Property Access

它解决什么问题

它把 obj.x 这种语法表示为运行时的属性读取操作。

教学版简化实现

plaintext 复制代码
regs[dst] = regs[objectReg][regs[propertyReg]];

Array Value

它解决什么问题

数组同样是运行时容器,只是元素按顺序保存。

教学版简化实现

plaintext 复制代码
const arr = [];
arr.push(1);

6. Mermaid 图解

flowchart TD A[Object Literal] --> B[OBJECT_NEW r0] B --> C[LOAD_CONST key x] C --> D[LOAD_CONST value 1] D --> E[OBJECT_SET r0 r1 r2]
flowchart TD A[MemberExpression obj.x] --> B[compile obj] A --> C[compile property key] B --> D[GET_PROP] C --> D

7. 伪代码

plaintext 复制代码
compileObjectExpression(obj):
    dst = allocReg()
    emit object_new dst
    for each property:
        key = compile key
        value = compile value
        emit object_set dst, key, value
    return dst

compileMemberExpression(expr):
    object = compile expr.object
    property = compile property key
    dst = allocReg()
    emit get_prop dst, object, property
    return dst

8. 教学版实现代码

plaintext 复制代码
const OPCODES = {
  OBJECT_NEW: 1,
  OBJECT_SET: 2,
  ARRAY_NEW: 3,
  ARRAY_PUSH: 4,
  GET_PROP: 5,
  SET_PROP: 6,
};

function executeObjectOpcode(opcode, code, state) {
  const { regs } = state;

  switch (opcode) {
    case OPCODES.OBJECT_NEW: {
      const dst = code[state.pc++];
      regs[dst] = {};
      break;
    }

    case OPCODES.OBJECT_SET: {
      const object = regs[code[state.pc++]];
      const key = regs[code[state.pc++]];
      const value = regs[code[state.pc++]];
      object[key] = value;
      break;
    }

    case OPCODES.GET_PROP: {
      const dst = code[state.pc++];
      const object = regs[code[state.pc++]];
      const key = regs[code[state.pc++]];
      regs[dst] = object[key];
      break;
    }
  }
}

9. 示例输入与输出

plaintext 复制代码
let obj = { x: 1 };
obj.x;

简化 IR:

plaintext 复制代码
object_new r0
load_const r1, 'x'
load_const r2, 1
object_set r0, r1, r2
init_slot slot0(obj), r0
load_slot r3, slot0(obj)
load_const r4, 'x'
get_prop r5, r3, r4
return r5

输出:

plaintext 复制代码
1

10. 执行过程拆解

Step Instruction Object State
1 object_new r0 {}
2 load_const 'x' {}
3 load_const 1 {}
4 object_set r0, 'x', 1 { x: 1 }
5 get_prop r5, r0, 'x' 返回 1

11. 与原始源码的差异

主题 教学版 正式源码
属性 key 字符串 支持 computed key
对象字面量 普通属性 还支持方法、getter/setter 转换
数组 简单 push 还处理 holes、spread 脱糖
new 本篇只说明 正式 runtime 用 Reflect.construct

12. 常见问题

Q1:obj.x 和变量 x 一样吗?

不一样。变量 x 从 environment 读取;obj.x 则先得到对象,再从这个对象上读取属性 x

Q2:为什么属性名也要放寄存器?

因为属性名不一定是固定字符串,它也可能是表达式,例如 obj[key]。只有先把 key 求值出来,运行时才能完成属性访问。

13. 本文小结

到这里,VM 已经能在运行时创建对象和数组,并通过属性 key 完成对象属性访问。

下一篇将继续讲:

plaintext 复制代码
第 11 篇:打包、模块与 CLI
相关推荐
泯泷1 小时前
手搓JSVM第 9 篇:闭包:函数如何记住外部变量
前端·javascript·前端框架
晴天161 小时前
前端 SSR、BFF 原理介绍
前端·javascript·网络·html
全栈技术负责人2 小时前
AI 效能工具建设:Agent 可观测性技能技术方案与实践
前端·ai·ai编程
梦想平凡2 小时前
百游棋牌源代码开发搭建教程(一):全端工程结构、开发环境配置与首次启动验证
前端·javascript·源代码管理
妙码生花2 小时前
两个月 59 篇 AI 开发日志 + Golang 商业级实战项目收工后,得来的 AI 使用心法-上
前端·后端·gin
vx-Biye_Design2 小时前
springboot中国传统节日宣传平台49078-计算机课程设计、毕业设计
java·前端·vue.js·spring boot·后端·课程设计·idea
妙码生花2 小时前
两个月 59 篇 AI 开发日志 + Golang 商业级实战项目收工后,得来的 AI 使用心法-下
前端·后端·go
Highcharts2 小时前
常见报错排雷指南3:兼容性问题的官方解法
javascript·数据可视化
5335ld3 小时前
app版本更新(vue3+unibest+静默更新+强制更新)
开发语言·javascript·ecmascript