4

下面是来自 Douglas Crockford 的 The Good Parts 的代码。

在大多数情况下,代码是有意义的。除了,我在这里不明白这一行:

var walk_the_DOM = function walk(node, func) {

看起来这个函数有两个名字 -walk_the_dom()walk()

再往下,您可以看到代码实际上是双向调用的,因此这两个名称实际上都引用了该函数。

为什么这个函数有两个名字?

// Define a walk_the_DOM function that visits every
// node of the tree in HTML source order, starting
// from some given node. It invokes a function,
// passing it each node in turn. walk_the_DOM calls
// itself to process each of the child nodes.

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

        // walk() called here

        walk(node, func);
        node = node.nextSibling;
    }
};

// Define a getElementsByAttribute function. It
// takes an attribute name string and an optional
// matching value. It calls walk_the_DOM, passing it a
// function that looks for an attribute name in the
// node. The matching nodes are accumulated in a
// results array.

var getElementsByAttribute = function (att, value) {
    var results = [];

   // walk_the_DOM() called here

    walk_the_DOM(document.body, function (node) { 
        var actual = node.nodeType === 1 && node.getAttribute(att);
        if (typeof actual === 'string' &&
                (actual === value || typeof value !== 'string')) {
            results.push(node);
        }
    });

    return results;
};
4

3 回答 3

5

我想这是为了让递归安全地工作。

例如,如果您只想使用walk_the_DOM名称,则该变量可能会在以后重新分配,或者由于范围而无法访问,因此在函数本身内部使用它是不安全的。

更新:

我做了一些研究,这就是我发现的。首先,请参阅 ECMAScript 5 规范,第 13 节。定义函数有两种方法:a) 使用FunctionDeclaration和 b) 使用FunctionExpression

它们看起来非常相似,但也有些不同。以下是FunctionDeclaration

function f() { };

这两个都是FunctionExpression

var x = function() { };
var y = function f() { };

对我们来说有趣的部分是关于FunctionExpression。在 的情况下var y = function f() { },标识符f函数体内可见。换句话说,在 , 之外的任何地方{ }都会typeof f返回undefined

现在是时候举一些实际的例子了。假设我们想使用FunctionDeclaration编写一个递归函数:

function f1(x) { x > 5 ? console.log("finished") : f1(x + 1) };

现在我们想将函数“复制”到另一个变量并设置f1为其他变量:

var f2 = f1;
var f1 = function() { console.log("Kitteh") };

但这并没有按预期工作:

f2(1); // outputs: Kitteh

现在,如果您使用 Douglas Crockford 定义递归函数的方式:

var f1 = function myself(x) { x > 5 ? console.log("finished") : myself(x + 1) };

这样,您可以根据需要多次将函数重新分配给任何变量。同时,我们确保函数总是调用自己,而不是分配给变量的某个函数f1

所以最初的问题的答案是:你以这种方式定义递归函数,因为它是最灵活和最健壮的方式。

于 2012-09-10T18:42:47.140 回答
1

因为walk函数是递归的,所以需要在匿名函数作用域内外调用:

var test = function test_recursive( num ) {
    if( num < 10 ) {
        test_recursive( --num );
    }
}

var num = test( 5 );
于 2012-09-10T18:46:13.353 回答
1

walk_the_DOM是你应该从外部调用函数的方式。

但是,如果函数想要使用递归,并且为了节省字节和/或写得更快,他们称之为walk.

编辑:正如 Shedal 指出的那样,该变量walk_the_DOM可以稍后重新分配。因此该功能将不再起作用。

于 2012-09-10T18:46:24.063 回答