前面博文有介绍JavaScript中数组的一些特性,通过对这些数组特性的深入梳理,能够加深我们对数组相关知识的理解,详见博文:
一文搞懂JavaScript数组的特性
其实,在前端开发中,除了数组以外,还有一种类似数组的对象,一般叫做类数组、或伪数组,也是我们需要掌握的知识点。
类数组是什么?
首先,我们先尝试给类数组加个简单的定义:拥有length属性的对象(非数组)。类数组的核心特征就是拥有length属性,拥有length属性又不是真正数组的对象,基本可以被认定为类数组。
虽然这个定义很简单,只突出了length属性,但类数组的基本特点,我们还是可以总结如下:
- 拥有length属性
- length属性非动态值,不会随元素变化而变化
- 可以使用数字索引下标访问属性元素
- 不是数组,没有数组对应的各种方法
- 可以使用for语句等进行循环遍历
- 能够通过一定方式转换成真正的数组
创建类数组的对象
根据以上类数组的特点,我们可以自己创建类数组的对象,也比较简单,只需要拥有length,如下所示:
const al = { 0: 111, 1: 222, 2: 333, length: 3 }
for (let i = 0; i < al.length; i++) {
console.log(al[i])
}
// 111
// 222
// 333
以上代码,创建了一个length属性为3的对象,通过for循环遍历对象,并且下标输出对应的值。
length属性并不会动态添加,比如我们给al对象增加一个属性,但length值仍为3:
al[3] = 444
console.log(al.length) // 3
如果把该类数组对象转换成一个真正的数组后,就可以进行数组操作了。
事实上,我们可以省略其他属性,只保留length,同样可当做一个类数组使用:
const al = { length: 3 }
虽然我们可以通过类似这种方式,自己创建类数组,但实际开发过程中,几乎没人这么处理。
真正在前端开发中,接触到的类数组,都是JavaScript语言或者Web环境下提供的各种类数组的对象。
在介绍这些类数组对象之前,我们先看下类数组对象如何才能转换成数组。
如何将类数组转换成数组
类数组只是类似数组的对象,缺少很多相应的方法,有时候我们可能需要把类数组转换成数组,才能更好的操作,这时候就需要找到方便有效的转换方式。
当前常用的转换方式,大致有如下几类,我们一个个介绍。
Array.from()
首先是ES6提供的新的数组静态方法:Array.from,它用于从一个类数组或可迭代对象中创建一个新的数组。基本上,只要拥有length属性的对象,都能被Array.from转换成数组。
语法:Array.from(arrayLike, mapFn, thisArg)
。
三个参数说明:
- arrayLike:类数组对象,或可迭代对象
- mapFn:可选参数,新数组会经过该函数处理后返回
- thisArg:可选参数,执行mapFn时指定的this指向
下面看一个示例:
const al = { 0: 111, 1: 222, 2: 333, length: 3 }
const arr = Array.from(al)
console.log(arr) // [111, 222, 333]
console.log(arr.length) // 3
console.log(Array.isArray(arr)) // true
以上代码,我们自定义了一个拥有length属性的对象,然后通过Array.from方法进行转换,得到了一个长度为3的新数组。
这种方式简单方便,在当前前端开发的绝大多数情况下,都应该是首选。
Array.prototype.slice.call()
在ES6没出来之前,如果想较好的进行类数组的转换,一般使用的是该方法:Array.prototype.slice.call()。
它基于数组的slice()方法,把执行时的this指向类数组对象,让类数组对象像数组一样使用slice()方法返回一个新的数组,因此拥有length属性的类数组对象都能被转换成数组。
const al = { 0: 111, 1: 222, 2: 333, length: 3 }
const arr = Array.prototype.slice.call(al)
console.log(arr) // [111, 222, 333]
以上代码,与Array.from定义同样的类数组对象,通过Array.prototype.slice.call转换,效果是一样的。
其他方式
以上两种方式,是最正确有效的转换,推荐日常使用。
除此以外,还有其他的方式,但各有缺陷,要么较复杂、要么对部分类数组对象不适用,所以并不太推荐。
- for循环处理
定义一个新的空数组,通过循环遍历类数组对象的属性,将类数组对象的每一个属性元素值都添加到新数组中。
这种方式就显得相对麻烦一些。 - 扩展运算符(...)
扩展运算符主要作用于可迭代对象,JavaScript中的集合(数组、Set、Map)都是可迭代对象,另外字符串和arguments也都是。
扩展运算符对于不可迭代对象,会报错。
[...'hello'] // ['h', 'e', 'l', 'l', 'o']
[...{ length: 3 }] // Uncaught TypeError: {(intermediate value)} is not iterable
Array.from({ length: 3 }) // [undefined, undefined, undefined]
以上代码,通过扩展运算符,字符串能被转换成数组,但自定义类数组对象 { length: 3 }
则不行,并报错;而使用Array.from则转成了拥有3个 undefined
元素的数组。
类数组对象
前端开发中最常见且被认可的类数组对象,是函数的arguments参数对象,另外字符串也是一个类数组对象,其他的还有各种Web环境提供的API。
如果把这些分成两类的话:
- JS对象
属于JavaScript语言的对象,在nodejs中也存在。
如arguments、字符串、TypeArray、自定义类数组等。 - Web对象
依赖于Web环境(一般是浏览器)的对象,不属于JavaScript语言。
如FileList、DOM列表对象、数据存储(如localStorage、sessionStorage)等。
下面我们就一一介绍下这些类数组对象。
JavaScript类数组对象
arguments
arguments是JS中函数内部的一个对象,用于处理不定数目的参数,是一个类数组对象。
在ES6推出之前,arguments的使用还是非常广泛的,但在ES6推出默认参数和扩展参数后,它的使用就减少了。
通过下面的示例,转成数组:
function test () {
console.log(Array.prototype.toString.call(arguments))
console.log(Array.from(arguments))
console.log([...arguments], Array.prototype.slice.call(arguments))
}
test(23)
// [object Arguments]
// [23]
// [23] [23]
以上代码可知,该对象存在独特的类型判断 Arguments
,可用扩展运算符处理arguments对象。
注意,箭头函数内部不存在arguments对象。
字符串
JavaScript中的字符串也是一个类数组对象,它拥有length属性,可以通过下标数字索引访问,也可以转换成数组,字符串中的每一个字符都变成新数组的一个元素。
const str = 'abc'
str.length // 3
str[1] // 'b'
Array.from(str) // ['a', 'b', 'c']
[...str] // ['a', 'b', 'c']
字符串拥有一个实例方法 split
,可以将字符串按照传入的分隔符进行处理,返回一个分割出来的每个子字符串都作为元素的数组:
'abc'.split('') // ['a', 'b', 'c']
'abc'.split('b') // ['a', 'c']
以上代码,
当使用空字符做分隔符的时候,就是将每一个字符都分割成数组元素,与类数组的转换方式相同。
当使用 b
字符做分隔符的时候,返回的数组元素就只有2个了。
由于split操作分隔符的灵活性,可以给我们处理字符串带来方便,所以常用该方法,而少用类数组转换。
TypeArray类型数组
类型数组也是一种类数组对象,提供了访问内存缓冲区中二进制数据的机制,它拥有11个对象,如Uint8Array、Uint8ClampedArray、Int32Array、Float64Array等等。具体知识可见博文前端二进制API知识总结
类型数组都拥有和数组相同的大部分用于遍历的实例方法,但它不是真正的数组,它不能动态变化,不支持push、pop、shift、unshift等修改数组的方法,类型判断也不是数组:
const u8 = new Uint8Array()
u8 instanceof Uint8Array // true
Array.isArray(u8) // false
[...u8] // []
Array.from(u8) // []
以上代码,可以看出,类型数组并不是真正的数组,但可以通过扩展运算符转换成数组(其他方式也行)。
当然,类型数组的使用场景一般在于处理二进制数据,能够遍历读取数据做一些操作即可,并不太需要做转换。
其他JS类数组对象
-
自定义类数组对象
前面已有介绍,开发中几乎不用。 -
function
函数作为一个对象,也拥有length属性,所以它也可以转换成数组。
函数的length属性表示的是参数的个数,转成数组后,就表示有几个元素,但元素值都为undefined。const fun = (al) => {} fun.length // 1 Array.from(fun) // [undefined]
Web-API类数组对象
FileList
FileList对象是一个我们常常用到的类数组对象,主要是在文件上传的过程中,我们选择文件后,前端用于接收文件信息的对象,就是它,可以读取到单个或多个文件数据。
FileList是一个拥有length属性并且属性索引值是数字的对象,它的每一个子成员属性都是一个 File
对象,处理文件信息。
inputFile.addEventListener('change', (e) => {
const files = e.target.files
console.log(Array.from(files))
})
// [File]
以上代码,就是监听一个上传控件的change事件,读取到对应的文件列表,并转换输出为数组,元素为File对象。
一般也用不着转换,直接遍历读取FileList即可。
DOM中类数组
在Web开发中,DOM操作也有很多类数组对象,如元素集合HTMLCollection、节点NodeList等等。
HTMLCollection
HTMLCollection来自于页面的document等节点元素对象,主要是各种属性:
- document.links:返回所有链接元素
- docuement.forms:返回所有表单元素
- document.images:返回所有图像元素
- document.scripts:返回所有脚本元素
- document.embeds:返回所有embed嵌入对象
- Element.children:返回当前节点的所有子元素
const links = document.links
Object.prototype.toString.call(links) // '[object HTMLCollection]'
const linkArr = Array.from(links) // []
Array.isArray(linkArr) // true
以上代码,读取了页面所有的链接,返回的一个HTMLCollection类数组对象,可以转换成对应的数组,当前页面并不存在链接,所以返回的是空数组。
NodeList
通过以下方法或属性获取的节点列表(NodeList):
- getElementsByTagName:返回所有拥有指定HTML标签名的节点
- getElementsByClassName:返回所有拥有指定class类属性名的节点
- getElementsByName:返回所有拥有指定name属性的节点
- querySelectorAll:返回所有匹配给定选择器的节点
- document.childNodes:返回所有子节点
const divList = document.querySelectorAll('div')
Array.isArray(divList) // false
Object.prototype.toString.call(divList) // '[object NodeList]'
divList.length // 4
const divArr = [...divList] // [div, div#clickInput, div#name, div#dropArea]
Array.isArray(divArr) // true
以上代码,通过 querySelectorAll
读取了页面上所有的div,得到了一个NodeList对象,是一个类数组对象,可以通过扩展运算符转换成一个真正的数组。
其他DOM相关类数组对象
以下也是DOM操作中,常见的一些类数组对象:
- document.styleSheets:返回所有样式表(StyleSheetList)
- attributes:返回节点元素的所有属性(NamedNodeMap)
- dataset:返回节点元素上的所有附加数据(DOMStringMap)
- classList:返回节点元素上的所有样式类(DOMTokenList)
DOM中的对象都是由DOM-API提供给JavaScript调用的,因为不止JS要用,所以使用类数组较合适。
其他Web类数组对象
-
window
window对象也拥有length,所以它也是类数组对象?
是的,window的length属性表示页面拥有的frame/iframe的数量。
所以也可以转换成数组:window.length // 0 Array.from(window) // []
当前页面没有frame,长度为0,转换成了空数组。
-
DataTransferItemList
用于从拖拽或粘贴事件中读取到的Datatransfer
对象的一个只读属性items
,同一级的只读属性还有files
,也是一个类数组对象FileList
,上面已有介绍。
在粘贴事件中,可以通过该对象读取到粘贴的数据内容。 -
数据存储
浏览器中的数据存储机制,如sessionStorage
和localStorage
,他们其实也是类数组对象,拥有length属性,可以进行转换。localStorage.length // 2 Array.from(localStorage) // [undefined, undefined]
以上代码,就是对localStorage的处理,拥有两个值,但由于是字符串键值对,无法转成数字索引,所以数组两个元素都是 undefined。文章来源:https://www.toymoban.com/news/detail-468107.html
总结
本文描述了什么是类数组对象,它的主要特点,以及如何创建一个类数组对象,还有将类数组对象转换成真正数组的几种方式,接着也介绍了前端开发中可能会遇到的已有的各种类数组对象。
事实上,绝大部分的类数组对象是不需要专门去转换为数组的,直接遍历操作即可,但深入掌握这些类数组的知识,也是前端开发中不可少的。文章来源地址https://www.toymoban.com/news/detail-468107.html
到了这里,关于一文搞懂前端的所有类数组类型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!