使用JSON可以吗?Stringify用于深度比较和克隆

Is it fine to use JSON.stringify for deep comparisons and cloning?

本文关键字:比较 深度 用于 Stringify JSON 使用      更新时间:2023-09-26

在尝试了几个json可序列化对象的深度比较和复制实现后,我注意到最快的通常只是:

function deep_clone(a){
   return JSON.parse(JSON.stringify(a));
};
function is_equal(a,b){
    return JSON.stringify(a) === JSON.stringify(b);
};

我觉得这是作弊。就像我将来会发现一些困扰我的问题。用这些可以吗?

JavaScript不保证键的顺序。

如果它们以相同的顺序输入,这种方法在大多数情况下是有效的,但它不可靠。

同样,对于深度相等但键按不同顺序输入的对象,它将返回false:

JSON.stringify({ a: 1, b: 2}) === "{"a":1,"b":2}"
JSON.stringify({ b: 2, a: 1}) === "{"b":2,"a":1}"

我意识到这是一个老问题,但我只是想添加更多的答案,因为有人可能会离开这个页面错误地认为使用JSON.stringify进行比较/克隆将没有问题,只要它不用于比较/克隆成员无序的对象。(公平地说,对于公认的答案,他们不应该认为;它说,"如果[成员]以相同的顺序输入,这种方法在大多数情况下都有效。")

代码可能最能说明潜在的问题:

JSON.stringify(NaN) === JSON.stringify(null)
// => true
JSON.stringify(Infinity) === JSON.stringify(null)
// => true
// or, to put it all together:
JSON.stringify({ val1: (1 / 0), val2: parseInt("hi there"), val3: NaN }) === JSON.stringify({ val1: NaN, val2: null, val3: null })
// => true
// and here's the same example with "cloning" rather than comparison:
JSON.parse(JSON.stringify({ val1: (1 / 0), val2: parseInt("hi there"), val3: NaN }))
// => Object {val1: null, val2: null, val3: null}

这些怪癖可能会引起麻烦,即使排序不是问题(正如其他人所说,这可能是问题)。在大多数情况下,这些怪癖不太可能出现,但最好注意到它们,因为它们可能导致一些很难发现的bug。

只要键值对总是按相同的顺序,是的,您可以使用stringify使用深层相等操作符(===)进行比较。

我写了这个函数来深度比较任何对象数组或值:如果你想使用它:)我用对象和数组中随机输入顺序的非常大的对象样本来测试它。

function c(x, y) {
  if (!x && !y) return !0
  if (!x || !y) return !1
  if (typeof (x) !==
      typeof (y)) return !1
  if (x instanceof Array) {
    if (
      x.length != y.length) return !1
    const f = []
    for (let i = 0; i < x.length; i++) {
      if (!c(x[i], y[i])) f.push(i)
    }
    const g = [...f]
    for (const i of f) {
      let r = !1
      for (const j of g) {
        if (
          c(x[i], y[j])) {
          g.splice(g.indexOf(j), 1)
          r++
          break
        }
      }
      if (!r) { return !1 }
    }
    return !0
  } else if (x instanceof Object) {
    const e1 =
          Object.entries(x)
    try {
      return c(e1, r(Object.entries(y),
        e1))
    } catch (e) {
      return !1
    }
  } else {
    return x === y
  }
  function r(
    u, v) {
    const a = []
    if (u.length != v.length) return u
    for (
      let i = 0; i < v.length; i++) {
      a.push(m(u, v[i][0]))
    }
    return a
  }
  function m(a, k) {
    for (let i = 0; i < a.length; i++) {
      if (a[i][0] === k) return [a[i][0], a[i][1]]
    }
    throw 0
  }
}