在不检查element .length的情况下,将可迭代元素或不可迭代元素扩展为数组

Expand an iterable element or non-iterable element into an array without checking element .length

本文关键字:元素 迭代 扩展 数组 element 不检查 length 情况下      更新时间:2023-09-26

给定html

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

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

调用document.querySelectorAll()返回一个具有.length属性的NodeList

.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")被扩展以填充可迭代数组。

通过将div设置为Array

的一个元素,我们可以在.querySelector()得到预期的结果。
[div]

at for..of iterable parameter.

对于.querySelector().querySelectorAll()使用相同模式的最接近的方法是使用Array.from()callback和变量的.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(),则将可迭代对象的内容扩展为数组;将.getElementsByTagName(), .getElementsByClassName(), .getElementsByTagName(), .getElementsByName()视为相同;或者将.querySelector()返回的单个值设置为数组的元素。

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

div.length ? div : [div]

如果div具有.length属性(可能是一个可迭代对象),则迭代div,尽管只有.length属性而不是iterable;

设置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 ?
  • 而不在同一行上引用元素三次?

能否接近工作解决方案

  • 得到改善;即div[Symbol.iterator]应该检查而不是.length吗?
  • 使用.spread elementrest element是否有魔力,可以允许遗漏检查对象的.length ?
  • 将使用Generator, Array.prototype.reduce()或其他方法改变需要检查.length[Symbol.iterator]属性的变量之前将元素扩展到一个数组?

或者,考虑到iterable或非iterable对象之间的差异,上述方法是最简单的方法吗?

我会或多或少地做Array.from所做的,但检查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
}