【问题标题】:Non recursive code going recursive非递归代码递归
【发布时间】:2017-05-13 14:37:56
【问题描述】:

以下代码应该创建一个简单的“树”,但它会变得递归并崩溃。

        var cnt = 0;
        var page = {
            properties: {},
            folders: [],
            items: []
        };

        function createPage() {
            var ret = $.extend({}, page);
            ret.cnt = cnt++;
            return  ret;
        }
        var tree = createPage();

        function addFolders(f, n) {
            for (var i = 0; i < n; i++) {
                f.push(createPage());
            }
        }


        function createData() {
            addFolders(tree.folders, 4);
            for (var i = 0; i < tree.folders.length; i++) {
                addFolders(tree.folders[i].folders, i);
            }
        }

        createData();

谁能解释一下为什么会这样?

【问题讨论】:

  • 我很困惑$.extend({}, page);这是在做什么?
  • 您的$.extend 没有进行深层复制,因此子文件夹始终是对相同根文件夹的引用。这样做var ret = $.extend(true, {}, page); 或直接在createPage() 中使用对象文字。
  • @squint 但是,由于它使用一个空对象作为目标,它实际上创建了一个副本......而不是引用。
  • @Black 一个复制对象的 jquery 函数,这里我用它来克隆一个对象而不是递归引用它(也许我做错了)
  • ...测试,将console.log(page.folders.length) 放入createPage()。您会看到 original page 对象的 folders 发生了变异。有一个计数器在达到100 或其他东西时抛出错误是个好主意,这样它就不会无限递归。此外,使用开发人员工具在代码运行时单步执行。

标签: javascript jquery function recursion tree


【解决方案1】:

squint 是对的,脚本总是推送到同一个folders 数组,导致长度不断增加并且永远不会退出循环。您可以将createPage 函数更改为:

function createPage() {
  return {
      properties: {},
      cnt: cnt++,
      folders: [],
      items: []
  };
}

【讨论】:

    【解决方案2】:

    $.extend({}, page);page 对象进行浅拷贝。

    var secondPage = $.extend({}, page);
    console.log(page === secondPage); // false
    console.log(page.folders === secondPage.folders); // true!!
    

    由于createData 循环遍历tree.folders,它也在修改它,所以每次迭代tree.folders.length 只会增加。

    您可以将附加选项传递给$.extenddo a deep extend $.extend(true, {}, page)

    var secondPage = $.extend(true, {}, page);
    console.log(page === secondPage); // false
    console.log(page.folders === secondPage.folders); // false
    

    【讨论】:

      【解决方案3】:

      问题: 您的 $.extend 没有进行深层复制,因此子文件夹始终是对相同根文件夹的引用。这样做var ret = $.extend(true, {}, page); 或直接在createPage() 中使用对象文字。它只做一个浅拷贝。引用两个数组和一个对象的属性不会被复制。这些是参考。

      复制/测试:console.log(page.folders.length) 放入createPage()。您会看到原始page 对象的folders 发生了变异。有一个计数器在达到100 或其他东西时抛出错误是个好主意,这样它就不会无限递归。此外,使用开发人员工具在代码运行时单步执行。

      解决办法: 原始外部对象保留在浅层。但是你不仅仅是在改变外部对象;您正在改变外部对象引用的对象。这就是我在上面展示的deep 选项的原因。因此,当您推送到副本的.folders 时,您正在推送到原始的.folders,因为它没有复制它;它分配了对外部副本的引用。

      这是一个更简单的演示:jsfiddle.net/zazvkvmh

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-07
        • 2022-12-06
        • 1970-01-01
        • 2018-03-17
        相关资源
        最近更新 更多