您在处理JavaScript数组时是否遇到过这样的错误?
Uncaught TypeError: children.forEach is not a function
forEach肯定是数组的函数,那么为什么我们会得到像上面这样的错误呢?有几种可能性,
- 您可能根本没有在数组上使用forEach。错误地,您可能在纯JavaScript对象或字符串等上使用了forEach。
- 您可能在您假定为数组的类似数组的对象上使用forEach,但事实并非如此。
在本文中,我们将了解类似JavaScript数组的对象以及如何处理它们。希望你觉得它有用。
什么是类似数组对象?
在JavaScript中,对象用于将多个值存储为复杂的数据结构。
使用花括号{…}和属性列表创建对象。属性是键值对,其中键必须是字符串,值可以是任何类型。
另一方面,数组是一个有序的集合,可以保存任何类型的数据。在JavaScript中,数组是用方括号[…]创建的,元素被索引。
类似数组是一个对象
- 具有对元素的索引访问权限和非负长度属性以知道其中元素的数量。这些是它与数组的唯一相似之处。
- 没有任何数组方法,如push、pop、join、map等。
这是一个类似数组的对象示例,
const arr_like = {0: 'I', 1: 'am', 2: 'array-like', length: 3};
如果你这样做
arr_like[2];
arr_like.length;
类似数组与普通数组完全不同。它不是由Array构建的,也不是使用Array literal[]构建的。因此它不会从Array.prototype继承任何东西。这就是我们在类似数组中看不到任何Array方法的原因。
length属性也不会自动更新。您不能像处理数组一样通过减少length属性值来缩小数组。
使用ES6,您可以轻松检查这一点,
Array.isArray(arr_like);
类似数组是一个相当普通的JavaScript对象。即使是普通的数组也是JavaScript中的对象
arr_like instanceof Object;
[] instanceof Object;
但是,你为什么需要知道呢?
JavaScript编程语言有许多类似数组的对象的用法。如果您不知道,您可能会将它们解释为数组并陷入可能的错误。一旦我们认识到一个类似数组的对象,我们还需要知道如何处理它
arguments是一个类似数组的对象
arguments 是一个类似数组的对象,可在包含传递给该函数的参数值的函数中访问。
function checkArgs() {
console.log(arguments);
}
让我们用几个参数调用这个函数,
checkArgs(1, 45);
浏览器控制台中的输出
你注意到上面输出中的__proto__值了吗?是的,它是一个对象,而不是数组。就像任何类似数组的对象一样,它有一个长度属性,并且值被索引。
function checkArgs() {
console.log(arguments.length);
}
现在让我们尝试在arguments上使用一些Array方法
function checkArgs() {
arguments.pop();
}
当我们尝试弹出参数的元素时,我们会得到以下错误,
试一试forEach怎么样?
function checkArgs() {
arguments.forEach((elem) => {
});
}
运气不好!我们会得到错误,
JavaScript中 HTMLCollection是一个类似数组的对象
另一个类似JavaScript数组的对象示例是DOM HTMLCollection。getElementsByTagName()等方法返回一个HTMLCollection。
让我们用一个例子来理解它,
<div id="main">
<ul>
<ol type="1">
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ol>
</ul>
</div>
现在,让我们尝试使用方法getElementsByTagName()查询DOM。在这个例子中,我们将使用标签li。
document.getElementsByTagName('li');
输出是
如你所见,它是一个HTMLCollection,看起来像一个数组。让我们扩展__proto__的值,看看HTMLCollection的类型是什么?
你看到了吗?是的,它也是一个对象。我们在上面试试怎么样?
document.getElementsByTagName('li').forEach(() => {
})
运气不好!这是因为HTMLCollection是一个类似Array的对象,并且没有任何Array方法可用。
如何处理类似数组?
在许多情况下,您可能希望将类似Array视为Array。它有一些优点。如果您可以将类似Array转换为Array,则可以使用所有数组方法进行计算。但是怎么做呢?
我们有三种方法可以实现它。
使用ES6扩展运算符
我们可以使用ES6扩展运算符([...array-like])将类似Array转换为Array。让我们再次重温arguments的示例。
function checkArgs() {
[...arguments].forEach((elem) => {
console.log(elem);
});
}
我们在arguments上使用扩展运算符,现在,允许在它上使用forEach。
试试
checkArgs(1,45);
输出
1
45
使用Array.from(类似数组)
您可以使用Array.from(array-like)将类似Array的转换为Array。
我们可以对我们的HTMLCollection示例执行以下操作,
const collection = Array.from(document.getElementsByTagName('li'))
如果你console.log(collection),你会在浏览器控制台中找到它,
请立即检查__proto__的值。它是一个数组。
使用slice方法
在前ES6时代,您可以使用slice()方法来进行转换。但是等等,slice()方法不是来自Array吗?我们将如何在类似Array的上使用它?看看这个
const args = Array.prototype.slice.call(arguments);
那里发生了一些事情。让我解释一下。
- Array.prototype使我们能够访问所有方法和属性。
- 我们不能直接调用slice()方法,this关键字指向数组,而不是arguments变量。
- call()是Function对象的原型方法。它允许我们更改this变量在函数内部指向的内容。
总结
- 类似数组不是数组。它们只是对元素和长度属性具有索引访问权限。与Array的所有相似之处都在这里结束
- 类似数组就像一个普通的JavaScript对象
- JavaScript语言有许多类似Array的对象,您最终可能会使用它们。
- 有三种方法可以将类似Array转换为Array,以便您可以正确处理它。使用扩展运算符 Array.from或slice()方法。