什么是类数组对象
简单来说,和数组类似,拥有
length
属性,可以通过索引来访问或设置里面的元素,但是不能使用数组的方法。
看个例子:
arr[0];
// => "dazhi"
这里的arr
一定是一个数组吗?不一定,也可能是一个对象。
let arr = {
0: 'dazhi'
}
console.log(arr[0]); // dazhi
再来看个例子:
let arr = ['name', 'age', 'job'];
// 创建一个类数组对象
let arrLike = {
0: 'name',
1: 'age',
2: 'job',
length: 3
}
注意:这边arrLike必须加上length
属性,不然它就是一个普通对象而已。
为什么叫做类数组对象呢?我们从读写、获取长度、遍历这三个方面来看看这两个对象。
读写
console.log(arr[0]); // name
console.log(arrLike[0]); // name
arr[0] = 'new name';
arrLike[0] = 'new name';
console.log(arr[0]); // new name
console.log(arrLike[0]); // new name
获取长度
console.log(arr.length); // 3
console.log(arrLike.length); // 3
遍历
for (let i = 0;i < arr.length; i++){
console.log(arr[i]);
}
// name
// age
// job
for (let i = 0;i < arrLike.length; i++){
console.log(arrLike[i]);
}
// name
// age
// job
有没有很像?当我们使用使用数组的方法呢?继续往下看:
arr.push('gender');
arrLike.push('gender'); // 报错
原形毕露了,终归是类数组。
那如果类数组
就想用数组的方法呢?可以把类数组
转换为数组。
转换为数组
Array.prototype.slice.call()
var arrLike2 = Array.prototype.slice.call(arrLike);
arrLike2.push('gender');
console.log(arrLike2[3]); // gender
Array.from()
var arrLike3 = Array.from(arrLike);
arrLike3.push('gender');
console.log(arrLike3[3]); // gender
Array.prototype.splice.call()
var arrLike4 = Array.prototype.splice.call(arrLike, 0);
arrLike4.push('gender');
console.log(arrLike4[3]); // gender
注意
如果length
值和实际元素不相等呢?
let arrLike = {
0: 'name',
1: 'age',
length: 3
}
console.log(Array.from(arrLike)); // ["name", "age", undefined]
可以看到,如果length
值大于实际元素的数量,不足的将用undefined
填充。
如果反过来呢?
let arrLike = {
0: 'name',
1: 'age',
length: 1
}
console.log(Array.from(arrLike)); // ["name"]
最终只保留了一个元素。可见,length
值是决定最终生成数组的长度的,多余的去掉,不足的用undefined
填充。
那如果我们索引不从0和1开始,可以吗?
let arrLike = {
2: 'name',
3: 'age',
length: 2
}
console.log(Array.from(arrLike)); // [undefined, undefined]
可见,0和1是有用的,会影响到最终的填充索引。
类数组对象的应用
说了这么多,类数组能用来做什么?
还记得arguments
对象吗?它就是一个类数组对象。我们看下MDN
上对其的描述:
Arguments对象
arguments
对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments
对象在函数中引用函数的参数。arguments
对象不是一个Array
。它类似于Array
,但除了length属性和索引元素之外没有任何Array
属性。例如,它没有pop
方法。但它可以被转换为一个真正的Array
。
MDN地址:developer.mozilla.org/zh-CN/docs/…
看个例子:
function foo(a, b, c) {
console.log(arguments);
}
foo(1, 2, 3);
控制台看下打印结果:
我们可以通过arguments
对象在函数内部引用传递进来的参数:
function foo(a, b, c) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
foo(1, 2, 3);
// 1
// 2
// 3
1.length属性
arguments
的length
属性代表的是实参的个数,因为我们调用函数的时候,有时候并不是所有参数都需要传入,看下面的代码:
function foo(a, b, c) {
console.log('实参的个数:' + arguments.length); // 1
}
foo(1);
console.log('形参的个数为:' + foo.length); // 3
2.auguments和对应参数的绑定
function foo(a, b, c, d) {
// "use strict";
console.log(a, arguments[0]); // 1 1
// 改变形参
a = 11;
console.log(a, arguments[0]); // 11 11
// 改变arguments
arguments[1] = 22;
console.log(b, arguments[1]); // 22 22
// 未传入的参数
console.log(c); // undefined
c = 3;
console.log(c, arguments[2]); // 3 undefined
arguments[3] = 4;
console.log(d, arguments[3]); // undefined 4
}
foo(1, 2);
总结:
- 有传入的参数,实参和
arguments
的值会共享,没有传入的参数,不会共享 - 在严格模式下,无论参数有没有传入,实参和
arguments
的值都不会共享
3.传递参数
将参数从一个函数传递到另一个函数。
function foo() {
bar.apply(this, arguments);
}
function bar(a, b, c) {
console.log(a, b, c);
}
foo(1, 2, 3);
// 1 2 3
4.arguments转为数组
arguments
除了可以用上面的几个转为数组的方法,还可以使用...
展开运算符。
function bar(a, b) {
// 用...把argumens转为数组
console.log([...arguments]); // [1, 2]
}
bar(1, 2);
但是如果应用到普通的类数组对象呢?
let arrLike5 = {
a: 1,
b: 2,
length: 2
}
console.log([...arrLike5]); // 报错 Uncaught TypeError: arrLike5 is not iterable
报错的意思是:arrLike5不是可迭代的,也就证实了arguments
除了是类数组对象,还是一个可迭代对象,而我们自定义的对象并不具备可迭代功能,所以不能使用展开运算符。
因为我们自定义的类数组对象不具备可迭代功能,所以也没办法使用for...of
来遍历:
let arrLike = {
0: 'name',
1: 'age',
2: 'job',
length: 3
}
for (let arrItem of arrLike) {
console.log(arrItem);
}
// 同样会报错 Uncaught TypeError: arrLike is not iterable
那么forEach
和for...in
呢?
forEach
是数组的方法,自然也没办法使用。
来看下for...in
:
let arrLike = {
0: 'name',
1: 'age',
2: 'job',
length: 3
}
for (let index in arrLike) {
console.log(index);
}
// 0
// 1
// 2
// length
for...in
是遍历对象的可枚举属性,会把length
也遍历出来。
所以只有for循环可以正确遍历类数组对象。
另外,arguments
的应用其实还有很多,这里就不继续展开了,大家有兴趣可以自己再去找资料学习一下,比如:
- 参数不定长
- 函数柯里化
- 递归调用
- 函数重载
- ...
其他类数组对象
我们在页面上随便写几个p
标签:
<p></p>
<p></p>
<p></p>
然后用document.getElementsByTagName()
获取:
var ps = document.getElementsByTagName('p');
console.log(ps);
可以看到,里面也有length
属性,但是它并不是一个数组,我们可以来检测一下:
// 类数组对象不能使用数组的方法
ps.push('a'); // 报错:Uncaught TypeError: ps.push is not a function
console.log(Object.prototype.toString.call(ps)); // [object HTMLCollection]
console.log(Object.prototype.toString.call([])); // [object Array]
好了,本文就先到这里了。
最后
感谢您的阅读,希望对你有所帮助。由于本人水平有限,如果文中有描述不当的地方,烦请指正,非常感谢。
关注
欢迎大家关注我的公众号前端帮帮忙
,一起交流学习,共同进步!
参考: