这是一个尊重内置 JSON.stringify() 规则同时限制深度的函数。此版本通过将循环引用设为 null 或使用可选回调来获取对象 ID(例如 GUID)来处理循环引用。
function stringify(val, depth, replacer, space, onGetObjID) {
    depth = isNaN(+depth) ? 1 : depth;
    var recursMap = new WeakMap();
    function _build(val, depth, o, a, r) { // (JSON.stringify() has it's own rules, which we respect here by using it for property iteration)
        return !val || typeof val != 'object' ? val
            : (r = recursMap.has(val), recursMap.set(val,true), a = Array.isArray(val),
               r ? (o=onGetObjID&&onGetObjID(val)||null) : JSON.stringify(val, function(k,v){ if (a || depth > 0) { if (replacer) v=replacer(k,v); if (!k) return (a=Array.isArray(v),val=v); !o && (o=a?[]:{}); o[k] = _build(v, a?depth:depth-1); } }),
               o===void 0 ? (a?[]:{}) : o);
    }
    return JSON.stringify(_build(val, depth), null, space);
}
var o = {id:'SOMEGUID',t:true};
var value={a:[12,2,{y:3,z:{o1:o}}],s:'!',b:{x:1,o2:o,o3:o}};
console.log(stringify(value, 0, (k,v)=>{console.log('key:'+k+';val:',v); return v}, 2));
console.log(stringify(value, 1, (k,v)=>{console.log('key:'+k+';val:',v); return v}, 2));
console.log(stringify(value, 2, (k,v)=>{console.log('key:'+k+';val:',v); return v}, 2));
console.log(stringify(value, 3, (k,v)=>{console.log('key:'+k+';val:',v); return v}, 2));
console.log(stringify(value, 4, (k,v)=>{console.log('key:'+k+';val:',v); return v}, 2, (v)=>{return v.id}));
{}
{
  "a": [
    12,
    2,
    {}
  ],
  "s": "!",
  "b": {}
}
{
  "a": [
    12,
    2,
    {
      "y": 3,
      "z": {}
    }
  ],
  "s": "!",
  "b": {
    "x": 1,
    "o2": {},
    "o3": null
  }
}
{
  "a": [
    12,
    2,
    {
      "y": 3,
      "z": {
        "o1": {}
      }
    }
  ],
  "s": "!",
  "b": {
    "x": 1,
    "o2": null,
    "o3": null
  }
}
{
  "a": [
    12,
    2,
    {
      "y": 3,
      "z": {
        "o1": {
          "id": "SOMEGUID",
          "t": true
        }
      }
    }
  ],
  "s": "!",
  "b": {
    "x": 1,
    "o2": "SOMEGUID",
    "o3": "SOMEGUID"
  }
(取自我在这里的帖子https://stackoverflow.com/a/57193068/1236397)
这是一个typescript版本:
/** A more powerful version of the built-in JSON.stringify() function that uses the same function to respect the
 * built-in rules while also limiting depth and supporting cyclical references.
 */
export function stringify(val: any, depth: number, replacer: (this: any, key: string, value: any) => any, space?: string | number, onGetObjID?: (val: object) => string): string {
    depth = isNaN(+depth) ? 1 : depth;
    var recursMap = new WeakMap();
    function _build(val: any, depth: number, o?: any, a?: boolean, r?: boolean) {
        return !val || typeof val != 'object' ? val
            : (r = recursMap.has(val),
                recursMap.set(val, true),
                a = Array.isArray(val),
                r ? (o = onGetObjID && onGetObjID(val) || null) : JSON.stringify(val, function (k, v) { if (a || depth > 0) { if (replacer) v = replacer(k, v); if (!k) return (a = Array.isArray(v), val = v); !o && (o = a ? [] : {}); o[k] = _build(v, a ? depth : depth - 1); } }),
                o === void 0 ? (a?[]:{}) : o);
    }
    return JSON.stringify(_build(val, depth), null, space);
}
注意:数组被视为字符串 - 原始值数组;因此,任何嵌套的对象项都被视为下一级,而不是数组对象本身(很像字符串可以是字符数组,但它是一个实体)。
更新:修复了空数组呈现为空对象的错误。