【问题标题】:Expand an iterable element or non-iterable element into an array without checking element .length将可迭代元素或不可迭代元素扩展为数组而不检查元素 .length
【发布时间】:2016-10-23 01:55:01
【问题描述】:

给定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]

for..ofiterable参数处。

最接近的方法是对两者使用相同的模式,或者.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
  • 没有在同一行引用元素三次?

可以解决工作的方法

  • 得到改进;也就是说应该检查[Symbol.iterator]div 而不是.length
  • 使用.spread elementrest element 是否有魔法可以忽略检查对象的.length
  • 使用GeneratorArray.prototype.reduce() 或其他方法是否会改变在将元素扩展为数组之前检查变量的.length[Symbol.iterator] 属性的需要?

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

【问题讨论】:

  • 你想在这里做什么?
  • 使用相同的模式将函数体内传递给函数的参数展开为数组;变量是可迭代的还是不是可迭代的。确定div.length ? div : [div] 是否是用于检查参数是否可能具有可迭代属性的最简洁的模式。如果检查div.length 足以检查对象是否为iterable?给定参数是其中之一,例如 .querySelector() :不是可迭代的,.querySelectorAll() :可迭代的。
  • 这是不是说你想要一个函数来将一个对象转换为一个单元素列表(如果它还不是可迭代的)?
  • @Ryan 是的。 div.length ? div : [div] 比较简短,实现了这一点。虽然这是正确的方法吗?也就是说{abc:123, length:3} 有一个.length 属性,虽然不一定是iterable,也不是一个数组。
  • this 有帮助吗? isIterable(divs) ? divs : [divs]

标签: javascript arrays ecmascript-6 iterable


【解决方案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
}

【讨论】:

  • if (typeof length !== 'number') { return [items] } 的目的是什么?
  • @guest271314:如果不是数字,从 0 循环到它没有多大意义。不过,您可以将该检查更改为length === undefined。真的没有正确的方法在这里做鸭子打字。
  • 如果items 是可迭代的,是否可以在不调用Array.from() 的情况下返回items?在特定的 if 语句块中使用 Array.from() 的目的是什么?
  • @guest271314:当然,如果你想要一个可迭代而不是数组,你可以这样做。
  • fwiw 拼凑了一种方法,该方法利用generator functionyield* 表达式、try..catch 如果传递给生成器函数的参数不是可迭代对象,则检查捕获的错误,Array.from() 以产生可迭代对象或来自传递参数的数组stackoverflow.com/a/40199709
猜你喜欢
  • 2021-11-05
  • 2021-11-14
  • 2018-07-13
  • 2012-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-22
  • 2014-06-13
相关资源
最近更新 更多