将可迭代元素或不可迭代元素展开到数组中而不检查元素.length

时间:2016-10-23 01:55:02

标签: javascript arrays ecmascript-6 iterable

给定html

<div></div>
<div></div>

调用document.querySelector("div")会返回第一个div元素,其中.length不是返回值的属性。

致电document.querySelectorAll()会返回NodeList属性.length

.querySelector().querySelectorAll()的两个返回值之间的差异是前者不是可迭代的;尝试使用spread element将元素扩展为数组时会抛出错误。

在以下示例中,请考虑divdivs是函数调用正文中接收的参数。因此,就可以收集而言,无法确定变量是否被定义为Element.querySelector()Element.querySelectorAll()document.querySelector()document.querySelectorAll()的结果;此外,只能使用.querySelector()检查.querySelectorAll().length之间的差异。

var div = document.querySelector("div");
for (let el of div) {
  console.log(".querySelector():", el)
}
<div></div>
<div></div>

日志

Uncaught TypeError: div[Symbol.iterator] is not a function

var div = document.querySelectorAll("div");
for (let el of div) {
  console.log(".querySelectorAll():", el)
}
<div></div>
<div></div>

返回预期结果;也就是说,document.querySelectorAll("div")被扩展为填充可迭代数组。

我们可以通过将.querySelector()设置为div

的元素,在Array获得预期结果
[div]

for..of iterable参数。

最接近使用相同模式的.querySelector().querySelectorAll()正在使用callback Array.from().tagName变量,和spread element。 虽然这省略了可能使用.querySelector()调用的其他选择器,例如.querySelector("div.abc")

var div = document.querySelector("div");
var divs = document.querySelectorAll("div");
var elems = Array.from({length:div.length || 1}, function(_, i) {
  return [...div.parentElement.querySelectorAll(
    (div.tagName || div[0].tagName))
         ][i]
});

for (let el of elems) {
  console.log(".querySelector():", el)
}

elems = Array.from({length:divs.length || 1}, function(_, i) {
  return [...divs[0].parentElement.querySelectorAll(
    (divs.tagName || divs[0].tagName))
         ][i]
});

for (let el of elems) {
  console.log("querySelectorAll:", el)
}
<div></div>
<div></div>

由于其他原因,这无法提供足够的准确性; Element.querySelector()最初可以传递给函数而不是document.querySelector(),类似于.querySelectorAll(). Not sure if it is possible to retrieve the exact selector passed to。querySelector,All`而不修改本机函数?

如果使用.querySelectorAll(),所需的模式将接受变量,并将iterable的内容扩展为数组;这将同等对待.getElementsByTagName().getElementsByClassName().getElementsByTagName().getElementsByName();或者将.querySelector()返回的单个值设置为数组的元素。

注意,目前的工作解决方案是

div.length ? div : [div]

如果div具有div属性(可能是可迭代的),则迭代.length,但只有.length属性而不是iterable; else将div设置为数组的单个元素,可迭代。

var div = document.querySelector("div");
var divs = document.querySelectorAll("div");
var elems = div.length ? div : [div];

for (let el of elems) {
  console.log(".querySelector():", el)
}

var elems = divs.length ? divs : [divs];

for (let el of elems) {
  console.log("querySelectorAll:", el)
}
<div></div>
<div></div>

能否实现

  • 没有检查变量的.length
  • 没有在同一行上引用该元素三次?

可以采用工作解决方案

  • 待改进;那应该[Symbol.iterator] div来检查,而不是.length
  • 使用.spread elementrest element是否有魔力,可以省略对.length对象的检查?
  • 使用GeneratorArray.prototype.reduce()或其他方法是否需要在将元素扩展为数组之前检查变量的.length[Symbol.iterator]属性?< / LI>

或者,考虑到iterable或不iterable的对象之间的差异,上面的方法是否可能是最简单的?

1 个答案:

答案 0 :(得分:1)

我会做更多或更少what Array.from does,但请检查length的类型,而不是始终转换它:

const itemsOrSingle = items => {
    const iteratorFn = items[Symbol.iterator]

    if (iteratorFn) {
        return Array.from(iteratorFn.call(items))
    }

    const length = items.length

    if (typeof length !== 'number') {
        return [items]
    }

    const result = []

    for (let i = 0; i < length; i++) {
        result.push(items[i])
    }

    return result
}