一、什么是真数组(数组)
所谓真数组,其实可以直接称为:数组。
当一个对象具有以下特点时,可以称为数组:
- 可以按照索引的方式,存储多个数组
- 具有length属性,表示数组内数据的长度(个数)
- 对象的原型属性
__proto__
,指向Array
类,且可以使用Array
的原型对象prototype
身上的属性和方法,如:push,pop等。如图1:
二、什么是伪数组
所谓伪数组,当一个对象具有以下特点:
- 具有length(长度)属性;
- 可以使用索引对数据进行操作;
- 但是不能使用数组的方法,如push,pop等。如图2:
注意:
1.伪数组一般不会直接创建,而是通过一些js操作得到,如:document.getElementsByName()
等;
2.因为伪数组具有长度和索引,所以可以使用循环语句遍历;
3.并不是能使用部分数组的方法,就是称为真数组;如图3:
4.有些情况下,并不是将伪数组的原型__proto__
属性设置为Array
的原型对象prototype
,就可以使用数组的方法。如图4:
三、如何将伪数组转成真数组
如果在一些情况下,js给我们返回的是伪数组,但是我们想使用真数组的方法对之进行操作,那么此时就需要将伪数组转成真数组之后,才能继续使用。
接下来我们根据伪数组和真数组的区别和特点,对伪数组做一下改造,将伪数组转成真数组,以完成后续操作。
方法1:遍历:创建一个空数组,循环遍历伪数组,将遍历出的数据逐一放在空数组中
var ali = document.getElementsByTagName('li');
console.log(ali); // [li, li, li, li]
// ali.push("hello"); // TypeError: ali.push is not a function
var arr = []; // 先创建空数组
for(var i=0;i<ali.length;i++){ // 循环遍历伪数组
arr[i] = ali[i]; // 取出伪数组的数据,逐个放在真数组中
}
arr.push("hello");
console.log(arr); // [li, li, li, li, "hello"]
方法2:使用slice方法:利用Array原型对象的slice方法,配合apply,将slice中的this指向伪数组
var ali = document.getElementsByTagName('li');
console.log(ali); // [li, li, li, li]
// ali.push("hello"); // TypeError: ali.push is not a function
var arr = Array.prototype.slice.apply(ali);
arr.push("hello");
console.log(arr); // [li, li, li, li, "hello"]
方法3:利用ES6提供的Array的from方法
var ali = document.getElementsByTagName('li');
console.log(ali); // [li, li, li, li]
// ali.push("hello"); // TypeError: ali.push is not a function
var arr = Array.from(ali);
arr.push("hello");
console.log(arr); // [li, li, li, li, "hello"]
方法4:利用ES6提供的展开运算符(...)
var ali = document.getElementsByTagName('li');
console.log(ali); // [li, li, li, li]
// ali.push("hello"); // TypeError: ali.push is not a function
var arr = [...ali];
arr.push("hello");
console.log(arr); // [li, li, li, li, "hello"]
方法5:利用原型的复制:将伪数组的proto复制为Array的prototype。但是这种方法有局限性
- 手动创建具有索引和长度的对象,作为伪数组
var obj = {
0:"a",
1:"b",
2:"c",
length:3
}
console.log(obj); // {0: "a", 1: "b", 2: "c", length: 3}
// obj.push(); // TypeError: obj.push is not a function
obj.__proto__ = Array.prototype;
console.log(obj); // ["a", "b", "c"]
obj.push("hello");
console.log(obj); // ["a", "b", "c", "hello"]
- arguments也适用
function fn(){
var arg = arguments;
console.log(arg); // ["a", "b", ...]
// arg.push("hello"); // TypeError: arg.push is not a function
arg.__proto__ = Array.prototype;
arg.push("hello");
console.log(arg); // ["a", "b", "hello", ...]
}
fn("a","b");
- 选择器返回的元素集合不适用,因为就算将元素集合的原型改成了数组原型(如图5),但元素集合本身是只读的,依然不能修改
var ali = document.getElementsByTagName('li');
console.log(ali); // [li, li, li, li]
// ali.push("hello"); // TypeError: ali.push is not a function
ali.__proto__ = Array.prototype;
// ali.push("hello"); // Index property setter is not supported
图5
但是不是意味着,没有修改到原数组的方法就可以使用呢(注意图5,没有length属性)
ali.forEach(val => {
console.log(val);
});
// 会发现浏览器没执行,原因是此时ali缺少length或length为0
console.log(ali.length); // 0
需要提前获取ali的length,在修改原型之后再手动设置
var ali = document.getElementsByTagName('li');
console.log(ali); // [li, li, li, li]
// ali.push("hello"); // TypeError: ali.push is not a function
var len = ali.length; // 获取初始length
ali.__proto__ = Array.prototype;
ali.length = len; // 设置给修改原型之后的数组对象
// ali.push("hello"); // Index property setter is not supported
ali.forEach(val => {
console.log(val); // 遍历打印数组中的值
});
console.log(ali.length); // 4
小提示:选择器返回的元素数组,使用复制原型方法,还需要手动设置length属性,且不能使用会改变原数组的方法。
以上。
关于伪数组转真数组的方法或技巧,后期继续补充...
文中如果纰漏,错误,不合理,描述不清晰,不准确等问题,欢迎大家留言指正...
网友评论