这是作为面试问题提供给我的-没有得到这份工作,但我仍然想弄清楚。

目的是编写两个querySelectorAll函数:一个名为qsa1的函数用于由单个标签名称(例如divspan)组成的选择器,而另一个称为qsa2的函数则接受任意嵌套的标签选择器(例如作为p spanol li code)。

我很容易就得到了第一个,但是第二个有点棘手。

我怀疑,为了处理可变数量的选择器,适当的解决方案可能是递归的,但是我认为我会尝试先进行迭代的工作。到目前为止,这是我得到的:

  qsa2 = function(node, selector) {
    var selectors = selector.split(" ");
    var matches;
    var children;
    var child;
    var parents = node.getElementsByTagName(selectors[0]);
    if (parents.length > 0) {
        for (var i = 0; i < parents.length; i++) {
            children = parents[i].getElementsByTagName(selectors[1]);
            if (children.length > 0) {
                for (var i = 0; i < parents.length; i++) {
                    child = children[i];
                    matches.push(child); // somehow store our result here
                }
            }
        }
    }
    return matches;
  }


除了它不起作用的事实外,我的代码的第一个问题是它仅处理两个选择器(但它应该能够清除第一种,第二种和第四种情况)。

第二个问题是我无法返回正确的结果。我知道,就像在qsa1中一样,我应该返回与通过调用getElementsByTagName()函数获得的结果相同的结果,该函数“返回具有给定标记名的元素的实时NodeList”。创建一个数组并将Node推入或追加到数组并不会削减它。

如何计算正确的退货结果?

(对于上下文,可以在here中找到全部代码)

最佳答案

这是我会做的

function qsa2(selector) {
    var next = document;
    selector.split(/\s+/g).forEach(function(sel) {
        var arr = [];
        (Array.isArray(next) ? next : [next]).forEach(function(el) {
            arr = arr.concat( [].slice.call(el.getElementsByTagName(sel) ));
        });
        next = arr;
    });
    return next;
}


假设我们总是从文档开始作为上下文,然后将选择器拆分到空格(就像您已经在做的那样),然后遍历标记名。
在每次迭代中,只需覆盖外部的next变量,然后再次运行循环。
我使用了一个数组和concat将结果存储在循环中。

这有点类似于问题中的代码,但是应该注意,您永远不会创建数组,实际上matches变量是undefined,并且不能被推入。

10-07 17:24