JavaScript的类数组
1、什么是类数组
只包含使用从零开始,且自然递增的整数做键名,并且定义了length表示元素个数的对象,我们就认为它是类数组对象。不能直接调用数组对象,但是又和数组比较相似。
var array = ['zhangsan', 'lisi', 'zhaoliu']; var arrayLike = { 0: 'zhangsan', 1: 'lisi', 2: 'zhaoliu', length: 3 }
代码块中的arrayLike
对象就是一个类数组对象(包含了0、1、2三个索引和一个length属性)。
但这似乎并不准确,同样上面的代码,array
变量是一个真正的数组,他也包含了若干索引和length属性啊,为什么它就是数组而不是类数组呢?为了彻底搞清楚这个问题,我们从数据的读取、长度的获取和它们各自的遍历三个方面来对比。
类数组和数组的数据读取
还是以上面的代码块为例,我们分别对它们的一个值进行读取:
var arrayVal = array[0]); // name var arrayLikeVal = arrayLike[0]; // name array[0] = 'new name'; arrayLike[0] = 'new name';
可以得出一个结论:从数据获取和值设置的角度,无论是获取数据还是对对象属性值进行设定,用法是非常相似的。
类数组和数组长度的获取和自身遍历
// 长度获取 console.log(array.length); // 3 console.log(arrayLike.length); // 3 // 遍历 for(var i = 0, len = array.length; i < len; i++) { // ... } for(var i = 0, len = arrayLike.length; i < len; i++) { // ... }
类数组和数组的长度length值获取和自身的遍历运用是非常相似的。
需要注意的是,在遇到遍历的需求的时候,实际上数组的效率要比类数组要高很多,因此如果在遇到有遍历类数组的需求的时候(比如遍历一个NodeList集合中的所有元素),建议先将类数组转化成数组,在执行遍历以此优化性能。
方法调用
数组中给出了很多已有的方法方便我们对数组进行增、删、改、查等操作,以常用的数组追加方法Array.push()为例:
array.push('tianqi') // array = ['zhangsan', 'lisi', 'zhaoliu', 'tianqi'] arrayLike.push('tianqi') // arrayLike.push is not a function
类数组不存在push这个方法,事实上,除了上面所说的几个相同地方,数组中包含的用以操作数组的方法类数组都不存在
用call/apply方法进行调用
call和apply方法可以改变this指向,根据这一特性,我们可以把类数组this指向真正的数组上去,这样不就可以在类数组上使用数组的方法了么。以Function.call方法为例:
var arrayLike = { 0: 'name', 1: 'age', 2: 'sex', length: 3 } Array.prototype.join.call(arrayLike, ':'); // name:age:sex Array.prototype.map.call(arrayLike, function(item){ return `${item}-map`; }); // ["name-map", "age-map", "sex-map"]
// 这样,我们通过改变类数组的this指向,间接地使用了数组的方法
类数组转换成数组
第二种方法让类数组使用数组的方法是先将类数组转化成真正的数组,然后就可以顺理成章的使用数组方法了,不过但这实际上是归纳在类数组转换成数组的这一点上了,和类数组使用数组方法并没什么联系。
根据部分数组的方法调用后会返回一个新的数组这一特性,总结了几种可以将类数组转换成数组的方法
// 1. slice Array.prototype.slice.call(arrayLike); // ["name", "age", "sex"] // 2. splice Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"] // 3. ES6 Array.from Array.from(arrayLike); // ["name", "age", "sex"] // 4. concat Array.prototype.concat.apply([], arrayLike) // 5 ES6 ...运算符 作为函数参数的时候可以吧arguments转换成数组 function translateArray(...arguments) { // ... }
// 除去es6提供的两种方式,其他三种依据的是这些函数本身会返回一个新的数组原理。
最后总结
这一节,我们从代码的角度对比类数组与数组的用法异同,并总结了几种类数组转换成数组的方式以及类数组中使用数组的方法:
-
类数组在数值读取和设置、长度获取、自身遍历等方法用法相似。
-
类数组上不存在数组中的操作方法。
-
通过Function.call或者Function.apply方法改变this指向,可以间接在类数组中使用数组的方法。
-
根据数组中部分函数会返回一个新的数组这一特性,我们可以实现类数组到数组的转换或者实现数组的快速拷贝。
-
遍历数组的效率实际上比类数组高很多,因此在遍历一个类数组的时候,建议优化的方式是先转换成数组再行遍历需求。
原文链接:https://juejin.cn/post/6844903649030701070