1

我试图了解递归是如何工作的。下面的代码遍历 HTML 文档的节点。它调用一个函数,依次传递每个节点。有人可以解释一下它是如何分步完成的吗?谢谢。

var walk_the_DOM = function walk(node, func) {
    func(node);
    node = node.firstChild;
    while (node) {
        walk(node, func);
        node = node.nextSibling;
    }
}
4

3 回答 3

2

第一条语句是函数定义:

var walk_the_DOM = function walk(node, func) {
    ...
};

这将函数分配给walk_the_DOM。这个函数有两个参数:nodefuncnode是您要处理的节点,func也是您要应用的功能node

函数的第一行是func(node);. 这实质上意味着您正在对node. 例如,如果您walk_the_DOM这样调用:

walk_the_DOM(root, function(node) {
    console.log(node);
});

你会打电话

function(node) {
    console.log(node);
}

在每个节点上,作为打印出树中每个节点的效果。

下一行node = node.firstChild;基本上将 重新分配node给它的第一个孩子。您需要这样做的原因是因为您需要查看当前节点的每个子节点。当然,您还需要查看这些孩子的孩子,但我们稍后会谈到那部分。

现在我们进入while循环。这个while循环的条件是 just while(node),这只是意味着只要未定义或未定义,循环就会node运行null。在我们之前的声明中,我们做到了node = node.firstChild。如果当前节点没有子节点怎么办?然后node.firstChild将为空,因此我们甚至不会进入循环。我们将退出它并退出函数(记住这部分;如果当前节点没有子节点,我们将退出函数。这被称为递归函数的停止条件)。

现在在while循环内部,我们进行递归调用:walk(node, func);. 让我们暂时忽略这里发生的事情,然后转到下一行:node = node.nextSibling;. 在这里,我们将节点的下一个兄弟节点分配回变量node。实际上,我们正在迭代这个节点的兄弟姐妹。现在如果节点没有其他兄弟节点(即父节点只有一个子节点)怎么办?然后node将为空,我们将退出循环。

现在让我们回到递归调用walk(node, func)。在递归调用中,我们调用函数本身,这意味着函数的行为与本次迭代完全相同。此时您可能会想“但这不意味着这将永远执行吗?”。但它不会!为什么?还记得我前面提到的停止条件吗?在某些时候,您将传入一个没有子节点的节点,这意味着递归调用将退出并返回到下一行(node = node.nextSibling) 并且执行将正常进行。现在,如果您将 DOM 视为一棵树(它确实如此),这意味着您将尽可能地沿着一个分支向下移动,一旦到达终点,您就会回退一层,并检查以查看如果还有其他兄弟姐妹。如果有,你就尽可能地沿着那个分支走下去。如果没有,您会再退回一级并再次进行检查。这样,您就可以遍历整个 DOM 树。

于 2012-11-05T21:04:03.333 回答
1

这段代码的作用是在子树的每个元素上执行func函数。

您将一个节点传递给 walk 函数。它获取该节点的所有子节点,并在它们上调用 walk 方法。

每个节点都可以看作是一棵子树,并且walk函数的每次调用只解决一个较小的子树解决方案。因为它walks实际上并没有做任何事情。

最终node.firstChild将是null,并且由于nullfalsejavascript 相比,它不会进入循环。

于 2012-11-05T20:50:24.697 回答
0
// 1. you define a function
var walk_the_DOM = function walk(node, func) { 

    // 4. you call your callback
    func(node);
    // func.call(node); // inside func, 'this' will be node

    // 5. you get the first child
    node = node.firstChild;

    // 6. as long as 'node' is, so not false, 0, null, undefined or '',
    while (node) {

        // 7. recurse the function, jumps to step 4 (in a new closure of walk)
        walk(node, func);
        // walk_the_DOM(node, func);

        // 8. get the next child, going to step 6
        node = node.nextSibling;
    }

    // 9. no more siblings, current walk returns

}; // 2. the function is defined, by name 'walk' and in var 'walk_the_DOM'!

// 3. you call the function once, with an anonymous callback
walk(document, function (currentNode) { });
// walk_the_DOM(document, function (currentNode) { });
于 2012-11-05T21:01:32.007 回答