JavaScript: 字符串化循环引用对象的浅表副本?

JavaScript: Stringify a shallow copy of a circular referenced object?

有没有办法让下面的浅表副本只深一层?我有办法使用完全不同的设计来解决此问题,但我想知道是否有人将 运行 转换为我之前尝试转换为字符串的内容。

var SomeObjClass = function() {
    var self = this;
    this.group = {
        members: [self]
    };
};
var p = new SomeObjClass();
var str = JSON.stringify(p);

有点不清楚你在问什么,但如果你的目标是简单地字符串化一个圆形对象,你必须覆盖 toJSON 来指定你希望如何表示你的对象

function SomeObjClass () {
  var self = this;
  this.group = {
    members: [self]
  };
}

SomeObjClass.prototype.addMember = function(m) {
  this.group.members.push(m);
};

// when stringifying this object, don't include `self` in group.members
SomeObjClass.prototype.toJSON = function() {
  var self = this;
  return {
    group: {
      members: self.group.members.filter(function (x) {
        return x !== self
      })
    }
  };
}

var a = new SomeObjClass();
var b = new SomeObjClass();
a.addMember(b);

console.log(JSON.stringify(a))

这可能是我在没有看到您的更多代码的情况下能为您提供的最好帮助。我不知道您如何使用此代码,但无论如何,这可能不是您 class 的最佳设计。如果您分享 class 的其余部分和使用它的代码,我们可能会更有效地帮助您。

如果您查看有关 JSON.stringify 的 MDN 参考资料,您会看到它接受作为第二个参数的 replacer 函数。这个函数对于稍微按摩一下你想要字符串化的元素很有用。

它可以帮助您避免循环问题。

例如:

function avoidCircularReference(obj) {
  return function(key, value) {
    return key && typeof value === 'object' && obj === value ? undefined : value;
  };
}

var SomeObjClass = function() {
    var self = this;
    this.group = {
        members: [self, {a:'f', b: [self]}]
    };
};
var p = new SomeObjClass();
var str = JSON.stringify(p, avoidCircularReference(p));
console.log(str);

但是,正如文档中所述并显示 运行 示例:

Note: You cannot use the replacer function to remove values from an array. If you return undefined or a function then null is used instead.

因此您必须以某种方式处理这些空值。无论如何,您可以使用此功能并 "adapt" 满足您的需要。例如并应用于您的示例:

function avoidCircularReference(obj) {
  var removeMeFromArray = function(arr) {
    var index = arr.indexOf(obj);
    if (index > -1) {
      arr.splice(index, 1);
    }
  };

  return function(key, value) {
    if (Object.prototype.toString.call(value) === "[object Array]") {
      removeMeFromArray(value);
    }
    return value;
  };
}

var SomeObjClass = function() {
  var self = this;
  this.group = {
    members: [self, {
      a: 'f',
      b: [self]
    }]
  };
};
var p = new SomeObjClass();
var str = JSON.stringify(p, avoidCircularReference(p));
console.log(str);

为了解决问题并保持 JSON.stringify 简单,我使用以下方法(在我的脱水方法中)

public dehydrate(): string {
    var seenObjects = [];
    function inspectElement(key, value) {
        if (detectCycle(value)) {
            return '[Ciclical]';
        } else {
            return value;
        };
    };
    function detectCycle(obj): boolean {
        if (obj && (typeof obj == 'object')) {
            for (let r of seenObjects) {
                if (r == obj) {
                    return true;
                };
            };
            seenObjects.push(obj);
        };
        return false;
    };
    let json: string = JSON.stringify(this, inspectElement,'  ');
    return json;
};

请注意,虽然这是 TypeScript,但在方法内部使用强类型来实现结果会使我们有些困惑。

不幸的是,我不得不使用 for 而不是数组搜索,因为它对我不起作用。

这是一个对我有用的实现。它依赖于引用比较来实现相等性,这对于这个目的来说应该没问题。

我在替换器 return 值中包含有问题的对象的索引,这样原则上,您可以在反序列化时恢复循环对象。

function safeJsonStringify(value) {
  const visitedObjs = [];
  function replacerFn(key, obj) {
    const refIndex = visitedObjs.indexOf(obj);
    if (refIndex >= 0) return `cyclic-ref:${refIndex}`;
    if (typeof obj === 'object' && obj !== null) visitedObjs.push(obj);
    return obj;
  }
  return JSON.stringify(value, replacerFn);
}
// Take it for a spin:
const cyclic = { greeting: 'Hello!' };
cyclic.badRef = cyclic;
console.log(safeJsonStringify(cyclic));