【发布时间】:2016-10-23 01:55:01
【问题描述】:
给定html
<div></div>
<div></div>
调用document.querySelector("div") 返回第一个div 元素,其中.length 不是返回值的属性。
调用document.querySelectorAll() 会返回一个具有.length 属性的NodeList。
.querySelector()和.querySelectorAll()这两个返回值的区别在于前者不是可迭代的;尝试使用spread element 将元素展开为数组时会抛出错误。
在以下示例中,请考虑div 或divs 是函数调用主体中接收的参数。因此,据我们所知,无法确定变量是否定义为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 element或rest element是否有魔法可以忽略检查对象的.length? - 使用
Generator、Array.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