合并对象 递归注意对象的合并时机

发布于:2025-08-02 ⋅ 阅读:(14) ⋅ 点赞:(0)
在这里插入代码片<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <style>
    .test {
      border-width: 20px;
      width: 0;
      height: 0;
      border-color: transparent, green, transparent, transparent;
    }
  </style>
  <body>
    <div class="trignale">
      <script>
        const obj = {
          a: {
            // b: 1,
            // c: 2,
            d: { e: 5 },
          },

          c: 3,
        };

        function flatten(obj) {
          let res = {};

          function detail(obj, oldKey = "") {
            let sonObj = {};
            for (let key in obj) {
              let keyStr = `${oldKey ? oldKey + "." : ""}${key}`;
              if (obj.hasOwnProperty(key)) {
                console.log(typeof obj[key]);

                if (typeof obj[key] != "object") {
                  sonObj[keyStr] = obj[key];
                  console.log(res, "RES1");

                  res = { ...res, ...sonObj };
                  console.log(res, "RES2");
                } else {
                  //错误

                  console.log(res, "res1");

                  // res = { ...res, ...detail(obj[key], keyStr) };
                  //正确
                  // res = { ...detail(obj[key], keyStr), ...res };
                  let b = detail(obj[key], keyStr);
                  res = { ...res, ...b };
                  console.log(res, "res2");
                }
              }
            }
            console.log(sonObj, "sonObj");

            return sonObj;
          }
          detail(obj);
          console.log(res);
          return res;
        }
        flatten(obj);

        //错误方案是 res{c: 3}
        //正确的方案 {a.d.e: 5, c: 3}

        //两个方案的本质区别在于扩展顺序的不同,错误的方案是先展开res,此时还没有进入递归,res是空的{},后面虽然进入了递归也改变了res,但是展开运算在前面,时间线后面的无法去改变前面的所以是空的去展开
        //,然后添加detail(obj[key], keyStr).注意这个函数有可能是返回空的{}如a.d的时候就是返回空对象,因为逻辑是!= "object", sonObj[keyStr] = obj[key];也就是说只有object的时候sonObj只能是{},然后返回的也是空
        //这样就造成了 // res = { ...res, ...detail(obj[key], keyStr) }; 为空 也就丢掉了其他的属性
   
         //而正确方案都是让detail(obj[key], keyStr)去先执行,先进入递归,此时res已经被res = { ...res, ...sonObj }; 改变了,因此res会有所有的属性,就是在合并空值页无所谓了


         //总结闭包对象虽然可以持久的保存对象的变化,但是代码的顺序很重要,就是讲究一个时机,如果递归合并选择在改变对象之前去做处理,还是不会映射成改变后的值

         //千万要注意合并的顺序

        sonObj
      </script>
    </div>
  </body>
</html>




function flatten(obj) {
  let res = {};

  function detail(obj, oldKey = "") {
    if (Array.isArray(obj)) {
      // 处理数组
      obj.forEach((item, index) => {
        const keyStr = `${oldKey}[${index}]`;
        if (typeof item !== "object" || item === null) {
          res[keyStr] = item;
        } else {
          detail(item, keyStr);
        }
      });
    } else if (typeof obj === "object" && obj !== null) {
      // 处理对象
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          const keyStr = `${oldKey ? oldKey + "." : ""}${key}`;
          if (typeof obj[key] !== "object" || obj[key] === null) {
            res[keyStr] = obj[key];
          } else {
            detail(obj[key], keyStr);
          }
        }
      }
    }
  }

  detail(obj);
  return res;
}

// 测试包含数组的对象
const objWithArray = {
  a: {
    b: 1,
    c: 2,
    d: { e: 5 },
  },
  b: [1, 3, { a: 2, b: 3 }],
  c: 3,
};

console.log(flatten(objWithArray));