深度克隆时这个递归是如何工作的?

时间:2021-03-13 22:19:38

标签: javascript recursion

此代码来自https://javascript.info/object-copy

function cloneDeep(obj) {
  let clone = {};
  for (let key in obj) {
    if (typeof obj[key] === "object") {
      clone[key] = cloneDeep(obj[key]);
    } else {
      clone[key] = obj[key];
    }
  }
  return clone;
}

let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50,
  },
};

console.log(cloneDeep(user));

我不太明白这个递归在第 5 行是如何工作的。对于 obj 中的每个键(所以高度和大小),如果对象中有一个对象,它会再次运行 cloneDeep() EACH TIME 。在第 2 行,它再次初始化 clone = {}。

如果它的高度和宽度都运行 cloneDeep(),我无法理解为什么输出不是这样的:

{name: "John",
  sizes: {height: 182},
  sizes: {width: 50}
}

3 个答案:

答案 0 :(得分:3)

你应该把这看作是一个树数据结构问题,一个对象有 x 个子属性,每个子属性有 x 个更多的子属性,依此类推……对于每个孩子,都有一个由这个孩子生成的特定子树,所以你可以为每个孩子递归调用这个函数,因为每个孩子都有自己的属性树。要进一步了解此类算法,您应该查看Trees

答案 1 :(得分:1)

考虑一个 clone 函数,它可以正确地切换对象和数组 -

function clone(t)
{ switch(t?.constructor)
  { case Array:
      return t.map(v => clone(v))
    case Object:
      return Object
        .entries(t)
        .reduce
          ( (r, [k, v]) =>
              Object.assign(r, {[k]: clone(v)})
          , {}
          )
    default:
      return t
  }
}

现在我们设置一个 original 对象和该对象的 copy -

const original =
  {a: [{b:1}, {c:2}], d: {e: "f"}}
  
const copy =
  clone(original)

如果我们对 original 进行一些更改 -

original.a[0].b = 11
original.a[1].c = 22
original.a.push({z:3})
original.d.e = "ff"

它们不应出现在 copy -

console.log(original)
// {"a":[{"b":11},{"c":22},{"z":3}],"d":{"e":"ff"}}

console.log(copy)
// {"a":[{"b":1},{"c":2}],"d":{"e":"f"}}

展开下面的代码片段以在您自己的浏览器中验证结果 -

function clone(t)
{ switch(t?.constructor)
  { case Array:
      return t.map(v => clone(v))
    case Object:
      return Object
        .entries(t)
        .reduce
          ( (r, [k, v]) =>
              Object.assign(r, {[k]: clone(v)})
          , {}
          )
    default:
      return t
  }
}

const original =
  {a: [{b:1}, {c:2}], d: {e: "f"}}
  
const copy =
  clone(original)

// changes to original
original.a[0].b = 11
original.a[1].c = 22
original.a.push({z:3})
original.d.e = "ff"

console.log(JSON.stringify(original))
// {"a":[{"b":11},{"c":22},{"z":3}],"d":{"e":"ff"}}

console.log(JSON.stringify(copy))
// {"a":[{"b":1},{"c":2}],"d":{"e":"f"}}

答案 2 :(得分:0)

对那里正在发生的事情的象征性表示

let user = {      // is object, init cloneDeep0.clone0:{}
  name: "John",   // not object, set clone0 key/value         clone0={name: "John"}
  sizes: {        // is object, init cloneDeep1.clone1:{}
                  // set clone0 key/(clone1={}),              clone0={name: "John", sizes: (clone1={}}}
    height: 182,  // not object, set clone1 key/value         clone0={name: "John", sizes: (clone1={height: 182})}
    width: 50,    // not object, set clone1 key/value         clone0={name: "John", sizes: (clone1={height: 182, width: 50})}
  },
};
                  // return clone0={name: "John", sizes: (clone1={height: 182, width: 50})}

实际上该代码中有一个错误,它会将 null 值克隆为 {}

相关问题