5.为什么需要了解类似数组对象?

175 阅读5分钟

您在处理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()​方法。