v8-jit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

V8 JIT Optimization

V8 JIT优化

Use this skill when writing or optimizing performance-critical code paths in Next.js server internals — especially per-request hot paths like rendering, streaming, routing, and caching.
当你在Next.js服务器内部编写或优化性能关键型代码路径时——尤其是请求级热点路径,如渲染、流式传输、路由和缓存——可以使用本技巧。

Background: V8's Tiered Compilation

背景:V8的分层编译

V8 compiles JavaScript through multiple tiers:
  1. Ignition (interpreter) — executes bytecode immediately.
  2. Sparkplug — fast baseline compiler (no optimization).
  3. Maglev — mid-tier optimizing compiler.
  4. Turbofan — full optimizing compiler (speculative, type-feedback-driven).
Code starts in Ignition and is promoted to higher tiers based on execution frequency and collected type feedback. Turbofan produces the fastest machine code but bails out (deopts) when assumptions are violated at runtime.
The key principle: help V8 make correct speculative assumptions by keeping types, shapes, and control flow predictable.
V8通过多个层级编译JavaScript:
  1. Ignition(解释器)——立即执行字节码。
  2. Sparkplug——快速基线编译器(无优化)。
  3. Maglev——中阶优化编译器。
  4. Turbofan——全量优化编译器(基于推测和类型反馈)。
代码从Ignition开始,根据执行频率和收集到的类型反馈升级到更高层级。Turbofan生成最快的机器码,但当运行时违反假设时会退出优化(deopt)
核心原则:通过保持类型、形状和控制流的可预测性,帮助V8做出正确的推测假设。

Hidden Classes (Shapes / Maps)

隐藏类(Shapes / Maps)

Every JavaScript object has an internal "hidden class" (V8 calls it a Map, the spec calls it a Shape). Objects that share the same property names, added in the same order, share the same hidden class. This enables fast property access via inline caches.
每个JavaScript对象都有一个内部“隐藏类”(V8称之为_Map_,规范称之为_Shape_)。拥有相同属性名称且添加顺序一致的对象会共享同一个隐藏类,这通过内联缓存实现了快速属性访问。

Initialize All Properties in Constructors

在构造函数中初始化所有属性

ts
// GOOD — consistent shape, single hidden class transition chain
class RequestContext {
  url: string
  method: string
  headers: Record<string, string>
  startTime: number
  cached: boolean

  constructor(url: string, method: string, headers: Record<string, string>) {
    this.url = url
    this.method = method
    this.headers = headers
    this.startTime = performance.now()
    this.cached = false // always initialize, even defaults
  }
}
ts
// BAD — conditional property addition creates multiple hidden classes
class RequestContext {
  constructor(url, method, headers, options) {
    this.url = url
    this.method = method
    if (options.timing) {
      this.startTime = performance.now() // shape fork!
    }
    if (options.cache) {
      this.cached = false // another shape fork!
    }
    this.headers = headers
  }
}
Rules:
  • Assign every property in the constructor, in the same order, for every instance. Use
    null
    /
    undefined
    /
    false
    as default values rather than omitting the property.
  • Prefer factory functions when constructing hot-path objects. A single factory makes it harder to accidentally fork shapes in different call sites.
  • Never
    delete
    a property on a hot object — it forces a transition to dictionary mode (slow properties).
  • Avoid adding properties after construction (
    obj.newProp = x
    ) on objects used in hot paths.
  • Object literals that flow into the same function should have keys in the same order:
  • Use tuples for very small fixed-size records when names are not needed. Tuples avoid key-order pitfalls entirely.
ts
// GOOD — same key order, shares hidden class
const a = { type: 'static', value: 1 }
const b = { type: 'dynamic', value: 2 }

// BAD — different key order, different hidden classes
const a = { type: 'static', value: 1 }
const b = { value: 2, type: 'dynamic' }
ts
// 良好实践——形状一致,单一隐藏类转换链
class RequestContext {
  url: string
  method: string
  headers: Record<string, string>
  startTime: number
  cached: boolean

  constructor(url: string, method: string, headers: Record<string, string>) {
    this.url = url
    this.method = method
    this.headers = headers
    this.startTime = performance.now()
    this.cached = false // 始终初始化,即使是默认值
  }
}
ts
// 不良实践——条件性添加属性会创建多个隐藏类
class RequestContext {
  constructor(url, method, headers, options) {
    this.url = url
    this.method = method
    if (options.timing) {
      this.startTime = performance.now() // 形状分支!
    }
    if (options.cache) {
      this.cached = false // 又一个形状分支!
    }
    this.headers = headers
  }
}
规则:
  • 在构造函数中按相同顺序为每个实例分配所有属性。使用
    null
    /
    undefined
    /
    false
    作为默认值,而非省略属性。
  • 构造热点路径对象时优先使用工厂函数。单一工厂函数可避免在不同调用点意外创建形状分支。
  • 绝不要在热点对象上
    delete
    属性——这会强制转换为字典模式(属性访问变慢)。
  • 避免在热点路径对象的构造完成后添加属性(
    obj.newProp = x
    )。
  • 流入同一函数的对象字面量应保持相同的键顺序:
  • 当不需要属性名称时,对极小的固定大小记录使用元组。元组可完全避免键顺序问题。
ts
// 良好实践——键顺序相同,共享隐藏类
const a = { type: 'static', value: 1 }
const b = { type: 'dynamic', value: 2 }

// 不良实践——键顺序不同,隐藏类不同
const a = { type: 'static', value: 1 }
const b = { value: 2, type: 'dynamic' }

Real Codebase Example

真实代码库示例

Span
in
src/trace/trace.ts
initializes all fields in the constructor in a fixed order —
name
,
parentId
,
attrs
,
status
,
id
,
_start
,
now
. This ensures all
Span
instances share one hidden class.
src/trace/trace.ts
中的
Span
在构造函数中按固定顺序初始化所有字段——
name
parentId
attrs
status
id
_start
now
。这确保所有
Span
实例共享一个隐藏类。

Monomorphic vs Polymorphic vs Megamorphic

单态 vs 多态 vs 超态

V8's inline caches (ICs) track the types/shapes seen at each call site or property access:
IC StateShapes SeenSpeed
Monomorphic1Fastest — single direct check
Polymorphic2–4Fast — linear search through cases
Megamorphic5+Slow — hash-table lookup, no inlining
Once an IC goes megamorphic it does NOT recover (until the function is re-compiled). Megamorphic ICs also prevent Turbofan from inlining the function.
V8的内联缓存(ICs)会跟踪每个调用点或属性访问处识别到的类型/形状:
IC状态已识别形状数速度表现
Monomorphic(单态)1最快——单次直接检查
Polymorphic(多态)2–4较快——线性遍历匹配案例
Megamorphic(超态)5+较慢——哈希表查找,无法内联
一旦IC进入超态,就无法恢复(直到函数被重新编译)。超态IC还会阻止Turbofan内联函数

Keep Hot Call Sites Monomorphic

保持热点调用点为单态

ts
// GOOD — always called with the same argument shape
function processChunk(chunk: Uint8Array): void {
  // chunk is always Uint8Array → monomorphic
}

// BAD — called with different types at the same call site
function processChunk(chunk: Uint8Array | Buffer | string): void {
  // IC becomes polymorphic/megamorphic
}
Practical strategies:
  • Normalize inputs at the boundary (e.g. convert
    Buffer
    Uint8Array
    once) and keep internal functions monomorphic.
  • Avoid passing both
    null
    and
    undefined
    for the same parameter — pick one sentinel value.
  • When a function must handle multiple types, split into separate specialized functions and dispatch once at the entry point:
ts
// Entry point dispatches once
function handleStream(stream: ReadableStream | Readable) {
  if (stream instanceof ReadableStream) {
    return handleWebStream(stream) // monomorphic call
  }
  return handleNodeStream(stream) // monomorphic call
}
This is the pattern used in
stream-ops.ts
and throughout the stream-utils code (Node.js vs Web stream split via compile-time switcher).
ts
// 良好实践——始终使用相同参数形状调用
function processChunk(chunk: Uint8Array): void {
  // chunk始终是Uint8Array → 单态
}

// 不良实践——同一调用点传入不同类型
function processChunk(chunk: Uint8Array | Buffer | string): void {
  // IC会变为多态/超态
}
实用策略:
  • 在边界处标准化输入(例如,将
    Buffer
    转换为
    Uint8Array
    一次),并保持内部函数为单态。
  • 避免为同一参数同时传递
    null
    undefined
    ——选择一个哨兵值。
  • 当函数必须处理多种类型时,拆分为多个专用函数,并在入口点仅调度一次:
ts
// 入口点仅调度一次
function handleStream(stream: ReadableStream | Readable) {
  if (stream instanceof ReadableStream) {
    return handleWebStream(stream) // 单态调用
  }
  return handleNodeStream(stream) // 单态调用
}
这是
stream-ops.ts
及整个stream-utils代码中使用的模式(通过编译时切换器区分Node.js与Web流)。

Closure and Allocation Pressure

闭包与分配压力

Every closure captures its enclosing scope. Creating closures in hot loops or per-request paths generates GC pressure and can prevent escape analysis.
每个闭包都会捕获其外部作用域。在热点循环或请求级路径中创建闭包会产生GC压力,并可能阻止逃逸分析。

Hoist Closures Out of Hot Paths

将闭包提升到热点路径之外

ts
// BAD — closure allocated for every request
function handleRequest(req) {
  stream.on('data', (chunk) => processChunk(chunk, req.id))
}

// GOOD — shared listener, request context looked up by stream
const requestIdByStream = new WeakMap()
function onData(chunk) {
  const id = requestIdByStream.get(this)
  if (id !== undefined) processChunk(chunk, id)
}

function processChunk(chunk, id) {
  /* ... */
}

function handleRequest(req) {
  requestIdByStream.set(stream, req.id)
  stream.on('data', onData)
}
ts
// BEST — pre-allocate the callback as a method on a context object
class StreamProcessor {
  id: string
  constructor(id: string) {
    this.id = id
  }
  handleChunk(chunk: Uint8Array) {
    processChunk(chunk, this.id)
  }
}
ts
// 不良实践——每个请求都会分配一个闭包
function handleRequest(req) {
  stream.on('data', (chunk) => processChunk(chunk, req.id))
}

// 良好实践——共享监听器,通过流查找请求上下文
const requestIdByStream = new WeakMap()
function onData(chunk) {
  const id = requestIdByStream.get(this)
  if (id !== undefined) processChunk(chunk, id)
}

function processChunk(chunk, id) {
  /* ... */
}

function handleRequest(req) {
  requestIdByStream.set(stream, req.id)
  stream.on('data', onData)
}
ts
// 最佳实践——预分配回调作为上下文对象的方法
class StreamProcessor {
  id: string
  constructor(id: string) {
    this.id = id
  }
  handleChunk(chunk: Uint8Array) {
    processChunk(chunk, this.id)
  }
}

Avoid Allocations in Tight Loops

避免在紧凑循环中分配对象

ts
// BAD — allocates a new object per iteration
for (const item of items) {
  doSomething({ key: item.key, value: item.value })
}

// GOOD — reuse a mutable scratch object
const scratch = { key: '', value: '' }
for (const item of items) {
  scratch.key = item.key
  scratch.value = item.value
  doSomething(scratch)
}
ts
// 不良实践——每次迭代都分配新对象
for (const item of items) {
  doSomething({ key: item.key, value: item.value })
}

// 良好实践——复用可变临时对象
const scratch = { key: '', value: '' }
for (const item of items) {
  scratch.key = item.key
  scratch.value = item.value
  doSomething(scratch)
}

Real Codebase Example

真实代码库示例

node-stream-helpers.ts
hoists
encoder
,
BUFFER_TAGS
, and tag constants to module scope to avoid re-creating them on every request. The
bufferIndexOf
helper uses
Buffer.indexOf
(C++ native) instead of a per-call JS loop, eliminating per-chunk allocation.
node-stream-helpers.ts
encoder
BUFFER_TAGS
和标签常量提升到模块作用域,避免在每个请求中重新创建。
bufferIndexOf
辅助函数使用
Buffer.indexOf
(C++原生实现)而非每次调用的JS循环,消除了每个块的分配。

Array Optimizations

数组优化

V8 tracks array "element kinds" — an internal type tag that determines how elements are stored in memory:
Element KindDescriptionSpeed
PACKED_SMI
Small integers only, no holesFastest
PACKED_DOUBLE
Numbers only, no holesFast
PACKED_ELEMENTS
Mixed/objects, no holesModerate
HOLEY_*
Any of above with holesSlower (extra bounds check)
Transitions are one-way — once an array becomes
HOLEY
or
PACKED_ELEMENTS
, it never goes back.
V8会跟踪数组的“元素类型”——一个内部类型标签,决定元素在内存中的存储方式:
元素类型描述速度表现
PACKED_SMI
仅包含小整数,无空洞最快
PACKED_DOUBLE
仅包含数字,无空洞
PACKED_ELEMENTS
混合类型/对象,无空洞中等
HOLEY_*
上述任意类型但存在空洞较慢(额外边界检查)
转换是单向的——一旦数组变为
HOLEY
PACKED_ELEMENTS
,就无法恢复。

Rules

规则

  • Pre-allocate arrays with known size:
    new Array(n)
    creates a holey array. Prefer
    []
    and
    push()
    , or use
    Array.from({ length: n }, initFn)
    .
  • Don't create holes:
    arr[100] = x
    on an empty array creates 100 holes.
  • Don't mix types:
    [1, 'two', {}]
    immediately becomes
    PACKED_ELEMENTS
    .
  • Prefer typed arrays only when you need binary interop/contiguous memory or have profiling evidence that they help. For small/short-lived collections, normal arrays can be faster and allocate less.
ts
// GOOD — packed SMI array
const indices: number[] = []
for (let i = 0; i < n; i++) {
  indices.push(i)
}

// BAD — holey from the start
const indices = new Array(n)
for (let i = 0; i < n; i++) {
  indices[i] = i
}
  • 预分配已知大小的数组:
    new Array(n)
    会创建带空洞的数组。优先使用
    []
    push()
    ,或
    Array.from({ length: n }, initFn)
  • 不要创建空洞:在空数组上执行
    arr[100] = x
    会创建100个空洞。
  • 不要混合类型:
    [1, 'two', {}]
    会立即变为
    PACKED_ELEMENTS
  • 仅当需要二进制互操作/连续内存,或有性能分析证据表明有帮助时,才优先使用类型化数组。对于小型/短生命周期集合,普通数组可能更快且分配更少。
ts
// 良好实践——紧凑SMI数组
const indices: number[] = []
for (let i = 0; i < n; i++) {
  indices.push(i)
}

// 不良实践——从一开始就带空洞
const indices = new Array(n)
for (let i = 0; i < n; i++) {
  indices[i] = i
}

Real Codebase Example

真实代码库示例

accumulateStreamChunks
in
app-render.tsx
uses
const staticChunks: Array<Uint8Array> = []
with
push()
— keeping a packed array of a single type throughout its lifetime.
app-render.tsx
中的
accumulateStreamChunks
使用
const staticChunks: Array<Uint8Array> = []
并配合
push()
——在整个生命周期中保持单一类型的紧凑数组。

Function Optimization and Deopts

函数优化与去优化

Hot-Path Deopt Footguns

热点路径去优化陷阱

  • arguments
    object
    : using
    arguments
    in non-trivial ways (e.g.
    arguments[i]
    with variable
    i
    , leaking
    arguments
    ). Use rest params instead.
  • Type instability at one call site: same operation sees both numbers and strings (or many object shapes) and becomes polymorphic/megamorphic.
  • eval
    /
    with
    : prevents optimization entirely.
  • Highly dynamic object iteration: avoid
    for...in
    on hot objects; prefer
    Object.keys()
    /
    Object.entries()
    when possible.
  • arguments
    对象
    :以非平凡方式使用
    arguments
    (例如,使用变量
    i
    访问
    arguments[i]
    、泄漏
    arguments
    )。改用剩余参数。
  • 单个调用点的类型不稳定:同一操作同时处理数字和字符串(或多种对象形状),导致变为多态/超态。
  • eval
    /
    with
    :完全阻止优化。
  • 高度动态的对象遍历:避免在热点对象上使用
    for...in
    ;尽可能优先使用
    Object.keys()
    /
    Object.entries()

Favor Predictable Control Flow

偏好可预测的控制流

ts
// GOOD — predictable: always returns same type
function getStatus(code: number): string {
  if (code === 200) return 'ok'
  if (code === 404) return 'not found'
  return 'error'
}

// BAD — returns different types
function getStatus(code: number): string | null | undefined {
  if (code === 200) return 'ok'
  if (code === 404) return null
  // implicitly returns undefined
}
ts
// 良好实践——始终返回相同类型
function getStatus(code: number): string {
  if (code === 200) return 'ok'
  if (code === 404) return 'not found'
  return 'error'
}

// 不良实践——返回不同类型
function getStatus(code: number): string | null | undefined {
  if (code === 200) return 'ok'
  if (code === 404) return null
  // 隐式返回undefined
}

Watch Shape Diversity in
switch
Dispatch

注意
switch
调度中的形状多样性

ts
// WATCH OUT — `node.type` IC can go megamorphic if many shapes hit one site
function render(node) {
  switch (node.type) {
    case 'div':
      return { tag: 'div', children: node.children }
    case 'span':
      return { tag: 'span', text: node.text }
    case 'img':
      return { src: node.src, alt: node.alt }
    // Many distinct node layouts can make this dispatch site polymorphic
  }
}
This pattern is not always bad. Often the main pressure is at the shared dispatch site (
node.type
), while properties used only in one branch stay monomorphic within that branch. Reach for normalization/splitting only when profiles show this site is hot and polymorphic.
ts
// 注意——如果多种形状命中同一调用点,`node.type`的IC可能变为超态
function render(node) {
  switch (node.type) {
    case 'div':
      return { tag: 'div', children: node.children }
    case 'span':
      return { tag: 'span', text: node.text }
    case 'img':
      return { src: node.src, alt: node.alt }
    // 许多不同的节点布局会使该调度点变为多态
  }
}
这种模式并非总是不好。通常主要压力来自共享调度点(
node.type
),而仅在一个分支中使用的属性在该分支内保持单态。只有当性能分析显示该调用点是热点且为多态时,才需要进行标准化/拆分。

String Operations

字符串操作

  • String concatenation in loops is usually fine in modern V8 (ropes make many concatenations cheap). For binary data, use
    Buffer.concat()
    .
  • Template literals vs concatenation: equivalent performance in modern V8, but template literals are clearer.
  • string.indexOf()
    > regex
    for simple substring checks.
  • Reuse RegExp objects: don't create a
    new RegExp()
    inside a hot function — hoist it to module scope.
ts
// GOOD — regex hoisted to module scope
const ROUTE_PATTERN = /^\/api\//

function isApiRoute(path: string): boolean {
  return ROUTE_PATTERN.test(path)
}

// BAD — regex recreated on every call
function isApiRoute(path: string): boolean {
  return /^\/api\//.test(path) // V8 may or may not cache this
}
  • 现代V8中循环内的字符串拼接通常没问题(Rope结构使许多拼接操作成本很低)。对于二进制数据,使用
    Buffer.concat()
  • 模板字面量 vs 拼接:在现代V8中性能相当,但模板字面量更清晰。
  • string.indexOf()
    > 正则表达式
    :用于简单子字符串检查。
  • 复用RegExp对象:不要在热点函数内部创建
    new RegExp()
    ——将其提升到模块作用域。
ts
// 良好实践——正则表达式提升到模块作用域
const ROUTE_PATTERN = /^\/api\//

function isApiRoute(path: string): boolean {
  return ROUTE_PATTERN.test(path)
}

// 不良实践——每次调用都重新创建正则表达式
function isApiRoute(path: string): boolean {
  return /^\/api\//.test(path) // V8可能会缓存,也可能不会
}

Map
and
Set
vs Plain Objects

Map
Set
vs 普通对象

  • Map
    is faster than plain objects for frequent additions/deletions (avoids hidden class transitions and dictionary mode).
  • Set
    is faster than
    obj[key] = true
    for membership checks with dynamic keys.
  • For static lookups (known keys at module load), plain objects or
    Object.freeze({...})
    are fine — V8 optimizes them as constant.
  • Never use an object as a map if keys come from user input (prototype pollution risk + megamorphic shapes).
  • Map
    :对于频繁添加/删除操作,比普通对象更快(避免隐藏类转换和字典模式)。
  • Set
    :对于动态键的成员检查,比
    obj[key] = true
    更快。
  • 对于静态查找(模块加载时已知键),普通对象或
    Object.freeze({...})
    即可——V8会将其优化为常量。
  • 绝不要将对象用作映射如果键来自用户输入(存在原型污染风险 + 超态形状)。

Profiling and Verification

性能分析与验证

V8 Flags for Diagnosing JIT Issues

用于诊断JIT问题的V8标志

bash
undefined
bash
undefined

Trace which functions get optimized

跟踪哪些函数被优化

node --trace-opt server.js 2>&1 | grep "my-function-name"
node --trace-opt server.js 2>&1 | grep "my-function-name"

Trace deoptimizations (critical for finding perf regressions)

跟踪去优化操作(对查找性能回归至关重要)

node --trace-deopt server.js 2>&1 | grep "my-function-name"
node --trace-deopt server.js 2>&1 | grep "my-function-name"

Combined: see the full opt/deopt lifecycle

组合使用:查看完整的优化/去优化生命周期

node --trace-opt --trace-deopt server.js 2>&1 | tee /tmp/v8-trace.log
node --trace-opt --trace-deopt server.js 2>&1 | tee /tmp/v8-trace.log

Show IC state transitions (verbose)

显示IC状态转换(详细)

node --trace-ic server.js 2>&1 | tee /tmp/ic-trace.log
node --trace-ic server.js 2>&1 | tee /tmp/ic-trace.log

Print optimized code (advanced)

打印优化后的代码(高级)

node --print-opt-code --code-comments server.js
undefined
node --print-opt-code --code-comments server.js
undefined

Targeted Profiling in Next.js

Next.js中的针对性性能分析

bash
undefined
bash
undefined

Profile a production build

分析生产构建

node --cpu-prof --cpu-prof-dir=/tmp/profiles
node_modules/.bin/next build
node --cpu-prof --cpu-prof-dir=/tmp/profiles
node_modules/.bin/next build

Profile the server during a benchmark

在基准测试期间分析服务器

node --cpu-prof --cpu-prof-dir=/tmp/profiles
node_modules/.bin/next start &
node --cpu-prof --cpu-prof-dir=/tmp/profiles
node_modules/.bin/next start &

... run benchmark ...

... 运行基准测试 ...

Analyze in Chrome DevTools: chrome://inspect → Open dedicated DevTools

在Chrome DevTools中分析:chrome://inspect → 打开专用DevTools

Quick trace-deopt check on a specific test

快速检查特定测试中的去优化情况

node --trace-deopt $(which jest) --runInBand test/path/to/test.ts
2>&1 | grep -i "deopt" | head -50
undefined
node --trace-deopt $(which jest) --runInBand test/path/to/test.ts
2>&1 | grep -i "deopt" | head -50
undefined

Using
%
Natives (Development/Testing Only)

使用
%
原生函数(仅用于开发/测试)

With
--allow-natives-syntax
:
js
function hotFunction(x) {
  return x + 1
}

// Force optimization
%PrepareFunctionForOptimization(hotFunction)
hotFunction(1)
hotFunction(2) % OptimizeFunctionOnNextCall(hotFunction)
hotFunction(3)

// Check optimization status
// 1 = optimized, 2 = not optimized, 3 = always optimized, 6 = maglev
console.log(%GetOptimizationStatus(hotFunction))
启用
--allow-natives-syntax
后:
js
function hotFunction(x) {
  return x + 1
}

// 强制优化
%PrepareFunctionForOptimization(hotFunction)
hotFunction(1)
hotFunction(2) % OptimizeFunctionOnNextCall(hotFunction)
hotFunction(3)

// 检查优化状态
// 1 = 已优化, 2 = 未优化, 3 = 始终优化, 6 = maglev
console.log(%GetOptimizationStatus(hotFunction))

Checklist for Hot Path Code Reviews

热点路径代码审核清单

  • All object properties initialized in constructor/literal, same order
  • No
    delete
    on hot objects
  • No post-construction property additions on hot objects
  • Functions receive consistent types (monomorphic call sites)
  • Type dispatch happens at boundaries, not deep in hot loops
  • No closures allocated inside tight loops
  • Module-scope constants for regex, encoders, tag buffers
  • Arrays are packed (no holes, no mixed types)
  • Map
    /
    Set
    used for dynamic key collections
  • No
    arguments
    object — use rest params
  • try/catch
    at function boundary, not inside tight loops
  • String building via array +
    join()
    or
    Buffer.concat()
  • Return types are consistent (no
    string | null | undefined
    mixes)
  • 所有对象属性在构造函数/字面量中按相同顺序初始化
  • 未在热点对象上使用
    delete
  • 未在热点对象构造完成后添加属性
  • 函数接收一致的类型(单态调用点)
  • 类型调度在边界处进行,而非热点循环内部
  • 未在紧凑循环内分配闭包
  • 正则表达式、编码器、标签缓冲区使用模块作用域常量
  • 数组是紧凑的(无空洞,无混合类型)
  • 动态键集合使用
    Map
    /
    Set
  • 未使用
    arguments
    对象——改用剩余参数
  • try/catch
    在函数边界处,而非紧凑循环内部
  • 使用数组+
    join()
    Buffer.concat()
    构建字符串
  • 返回类型一致(无
    string | null | undefined
    混合)

Related Skills

相关技巧

  • $dce-edge
    — DCE-safe require patterns (compile-time dead code)
  • $runtime-debug
    — runtime bundle debugging and profiling workflow
  • $dce-edge
    —— DCE安全的require模式(编译时死代码消除)
  • $runtime-debug
    —— 运行时包调试与性能分析工作流