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

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

给定 html

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

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

调用 document.querySelectorAll() return 一个 NodeList 有一个 .length 属性.

.querySelector().querySelectorAll()的两个return值的区别在于前者不是可迭代的;尝试使用 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>

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

通过将 div 设置为 Array

的元素,我们可以在 .querySelector() 处获得预期结果
[div]

for..of iterable 参数。

最接近于对两者或 .querySelector().querySelectorAll() 使用相同模式的是使用 Array.from()callback.tagName.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() 编辑的单个值 return 设置为数组的元素。

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

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>

这能实现吗

能否提供解决方案的方法

或者,考虑到 iterable 或不 iterable 对象之间的差异,上述方法是否是最简短的方法?

我会做或多或少 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
}