牛耳教育吧 关注:340贴子:1,578
  • 0回复贴,共1

JS中的类数组对象怎么转换为数组

只看楼主收藏回复

web前端开发中的JS的类数组对象是什么?如何将类数组对象转为数组?

类数组对象是什么?
类数组对象,就是含有length属性的对象,但这个对象不是数组。通常来说还会有0~length-1的属性,结构表现上非常像一个数组。
const arrlike={1:'a',4:'d',length:9};
Array.isArray(arrlike)//false
从底层上来看,这个对象的原型链上没有Array.prototype,所以我们不能用Array.prototype上的forEach、map等数组特有的方法。
关于原型链,可以看我的这篇文章:《如何用原型链的方式实现一个JS继承?》
如果强行给一个类数组对象使用forEach方法,会得到一个类型错误。
functionf(a,b){
arguments.forEach(item=>console.log(item));
}
//UncaughtTypeError:arguments.forEachisnotafunction
除了手动创造的类数组对象,还有以下常见的类数组对象:
普通数组中的argument对象。需要注意的是,箭头函数中不存在这个对象。
一些获取Dom集合的方法,如document.querySelectorAll()、document.getElementsByClassName、document.getElementsByTagName()也会返回类数组对象
下面看看我们有哪些将类数组转换为数组的方法。
Array.prototyle.slice.call()
我们可以用Array.prototyle.slice内置方法。
Array.prototype.slice.call(obj);
[].slice.call(obj);
[]空数组效果同Array.prototype,因为空数组本身没有slice方法,会走原型链拿到Array.prototype的方法。空数组写法除了短一点没有任何优点。
然后call来自Function.prototype,可以使用一个指定的this值来调用一个函数,这里是Array.prototype.slice。我们不给slice方法传开始和结束位置参数,这样就会默认取整个范围。
slice的迭代时会跳过不存在的索引属性,所以你会看到empty的特殊值,和undefined还有点不同,你可以认为表示未被初始化。forEach、map这些内置方法是会跳过它们不执行传入的回调函数的。一个例子:
const arrlike={1:'a',4:'d',length:9};
const arr=Array.prototype.slice.call(arrlike);
console.log(arr);

什么原理?因为slice用于拷贝返回一个新的子数组,且只需要被操作的目标有length属性就行了。下面是Array.prototype.slice的核心实现,默认start和end都在length范围内。
Array.prototype.mySlice=function(start,end){
if (start==undefined) start=0;
if (end==undefined) end=this.length;
const cloned=newArray(end-start);
for (leti=0;i<cloned.length;i++){
//为了确保不存在的索引保持为empty值
if (iinthis){
cloned[i]=this[start+i];
}
}
return cloned;
}
你会发现,类数组对象替换掉这里的this后,能拷贝出一个真正数组。
Array.from()
ES6新出的方法,可以将类数组对象或者是可迭代对象转换为数组。
const arrlike={1:'a',4:'d',length:9};
arr=Array.from(arrlike);
console.log(arr);

和Array.prototyle.slice.call()有点不同,不存在的索引的值被设置了undefined。
如果一个对象,既是类数组对象,又是可迭代对象,Array.from()方法会使用该对象的迭代器方法。
一般来说,调用JS的内置方法返回类数组对象同时是可迭代对象,我们通常喜欢用扩展运算符(...),更优雅。
const elCollection=document.getElementsByTagName('div');
const elArray=[...elCollection];
结尾
一般来说,我们在开发中遇到的类数组对象都是JS内置方法返回的,同时也是可迭代对象,我们一般都是用[...arrlik]扩展运算符,咻咻咻搞定。
如果类数组对象不是可迭代对象,可以使用Array.prototyle.slice.call()和Array.from()。
前者会对不存在的索引维持为empty,后者则是undefined。我们可以认为基本差别不大,建议用Array.from(),语义化更好些。


1楼2022-05-26 14:22回复