You need to enable JavaScript to run this app. 如何正确序列化MutationRecord对象?JSON.stringify返回空对象问题
优惠活动
大模型
产品
解决方案
定价
更多

如何正确序列化MutationRecord对象?JSON.stringify返回空对象问题

MutationRecord 序列化得到空对象的解决方法

问题根因

你遇到的JSON.stringify(mutation)输出{}的核心原因是:
MutationRecord 接口的所有核心属性(包括变更类型、节点列表、目标节点、旧值等)都定义在MutationRecord.prototype原型上,属于带 getter 的访问器属性,并非实例自身的可枚举属性。

JSON.stringify的序列化规则只会提取对象自身可枚举的属性,会直接跳过两类属性:

  • 沿原型链继承的属性
  • 非自身可枚举的属性

因此最终扫描不到任何有效字段,输出空对象。
console.log能正常展示完整结构,是因为浏览器控制台会自动递归展示对象原型链上的可访问属性,和序列化的执行逻辑完全不同。

正确实现方案

由于无法通过JSON.stringify直接读取原型链上的属性,需要手动提取MutationRecord的所有标准字段,构造一个普通的纯对象,再对这个纯对象做序列化。
同时需要注意:MutationRecord里包含的DOM节点、NodeList类型的值,无法直接序列化(会出现循环引用、空对象等问题),需要提前做适配转换。

通用序列化工具函数

function serializeMutationRecord(mutation) {
  // 适配DOM节点,转换为无循环引用的基础信息
  const serializeNode = (node) => {
    if (!node || !node.nodeType) return null;
    return {
      nodeType: node.nodeType,
      nodeName: node.nodeName,
      id: node.id || null,
      // 截断长文本,避免序列化结果冗余
      textContent: node.textContent?.slice(0, 50)
    }
  }

  // 适配NodeList,转换为序列化后的节点数组
  const serializeNodeList = (nodeList) => {
    return Array.from(nodeList || []).map(serializeNode)
  }

  // 手动提取MutationRecord所有标准属性
  return {
    type: mutation.type,
    target: serializeNode(mutation.target),
    addedNodes: serializeNodeList(mutation.addedNodes),
    removedNodes: serializeNodeList(mutation.removedNodes),
    previousSibling: serializeNode(mutation.previousSibling),
    nextSibling: serializeNode(mutation.nextSibling),
    attributeName: mutation.attributeName,
    attributeNamespace: mutation.attributeNamespace,
    oldValue: mutation.oldValue
  }
}

使用方式

在原来的MutationObserver回调代码中,把直接序列化的部分替换即可:

function callback(mutationList, observer) {
  mutationList.forEach( (mutation) => {
    // 原直接序列化代码 JSON.stringify(mutation) 会输出 "{}"
    // 替换为手动提取字段后的序列化逻辑
    const serializedMutation = JSON.stringify(serializeMutationRecord(mutation))
    console.log(mutation, serializedMutation)

    switch(mutation.type) {
      case 'childList':
        /* 节点树中新增或移除了子节点,可通过mutation.addedNodes和mutation.removedNodes查看详情 */
        break;
      case 'attributes':
        /* mutation.target元素的属性值发生变化,属性名存于mutation.attributeName,旧值存于mutation.oldValue */
        break;
    }
  });
}

const targetNode = document.querySelector("#someElement");
const observerOptions = {
  childList: true,
  attributes: true,
  // 省略(或设为false)则仅监听目标节点自身的变化
  subtree: true
}

const observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);

document.querySelector('#theButton').onclick = function() {
    targetNode.innerText = 'Random Value ' + Math.random()*100;
}

对应配套的HTML结构无需改动:

<p id="someElement">
Some Value
</p>
<button id="theButton">
  Update
</button>

拓展说明

类似的序列化问题并非MutationRecord独有。所有浏览器内置的Web API对象(包括DOM节点、CSSStyleDeclaration、原生事件对象等),核心属性大多定义在原型链上,直接调用JSON.stringify都会输出空对象,都需要通过手动提取字段、构造纯对象的方式完成序列化。

内容的提问来源于stack exchange,提问作者Jacksonkr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.02 16:06:03