JavaScript的for循环变量和递归

16

我有一个问题,我在for循环内部使用了递归:

function func(node) {
    for(var i = 0; i < node.children.length; i++) {
       func(node.children[i]);
    } 
} 

很明显,因为JavaScript没有块级作用域,所以每次调用函数时都会修改相同的i变量。有什么最好的解决方法?假设使用常规的EcmaScript 3,不能使用JavaScript 1.7中的"let"。

我知道这个问题以前已经被问过了,但是其他问题似乎没有展示递归,它们只展示了可以使用闭包的一个函数调用。


2
你能否发布一些node的示例数据吗?JavaScript将变量作用于包含它的function或对象字面量中,因此每个递归调用到func都应该获取自己的i - Stoive
同意。在其他情况下进行了测试。你能在jsfiddle.com上发布一些示例吗? - Liangliang Zheng
1
这个函数看起来只是简单地循环一下(递归),假设节点有子节点的话……它应该要做些什么吗? - Gerrat
如果您不想要文本节点,那么您的代码应该可以正常工作(在支持“children”的浏览器中)。只需在其中添加一些对“node”执行操作的代码即可。将此代码粘贴到控制台中,您将在所有内容上获得漂亮的橙色边框:function func(node) {for(var i = 0; i < node.children.length; i++) {if(node.nodeType === 1) node.style.border="1px solid orange";func(node.children[i]);}} func(document.body); - user113716
好的,这是一个无关的问题,其中子元素的长度被修改了。对于造成的困惑,我感到抱歉。 - mcot
7个回答

17

缓存数组的长度,这样你将会得到以下内容:

function recurse(node) {
    for(var i = 0, count = node.children.length; i < count; i++) {
        recurse(node.children[i]);
    }
} 

当你处理HTMLCollection时,应该始终使用缓存。


10

只需使用 Crockford 的 walkTheDOM 函数:

function walkTheDOM(node, func) {
    func(node);
    node = node.firstChild;
    while (node) {
        walkTheDOM(node, func);
        node = node.nextSibling;
    }
}

您需要传入根节点和您想要在每个节点上运行的函数,如下所示:
var root = document.getElementById('wrap');

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

Live demo: http://jsfiddle.net/VKWTt/


这仅适用于DOM元素,但问题是以一般方式提出的。 - F Lekschas
@FLekschas 它适用于所有类型的节点:https://jsbin.com/daruzo/edit?html,js,console - Šime Vidas
好的,我的措辞有误。节点可以是任何东西,不仅仅是 DOM 节点,例如一个普通对象:https://jsbin.com/yepoqixowe/edit?html,js,console - F Lekschas

7

是否遇到了这样的问题,在函数递归过程中,变量的值被替换。递归在for循环内部,所以for循环内部的变量被修改。

使用var声明在递归过程中会被修改的变量。


2
参考链接var 和不使用 var 的区别,会更详细地解释这个问题。 - karthick-sk

2

我认为你的例子应该可以工作。变量i被声明为局部变量,因此在递归时会使用新的“i”。

Javascript支持全局和局部变量!


2

你已经在更广泛的范围内定义了变量“i”;)


1

我有点困惑。i被本地声明,因此它不是被修改的同一个i变量。在这个页面上进行了测试:

var span = document.getElementsByTagName("span")[0];
function func(node) {
    for(var i = 0; i < node.children.length; i++) {
       console.log([i, node]);
       func(node.children[i]);
    } 
}
func(span);

// returns
// [0, <span id="hlinks-user">...</span>]
// [1, <span id="hlinks-user">...</span>]
// [2, <span id="hlinks-user">...</span>]
// [0, <a href="/users...">...</a>]
// [3, <span id="hlinks-user">...</span>]
// [0, <span title="1 silver...">...</span>]
// [1, <span title="1 silver...">...</span>]
// [4, <span id="hlinks-user">...</span>]
// [0, <span title="7 bronze...">...</span>]
// [1, <span title="7 bronze...">...</span>]
// [5, <span id="hlinks-user">...</span>]

0

这对我有用。

 function DragDropChanges(nodeChanged) {
        if (nodeChanged.children != null) {
            for (i = 0; i < nodeChanged.children.length;
                var temp = i;
                DragDropChanges(nodeChanged.children[i]);
                i = temp;
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接