【前端帮帮忙】第10期 浅析类数组对象

什么是类数组对象

简单来讲,和数组相似,拥有length属性,能够经过索引来访问或设置里面的元素,可是不能使用数组的方法。javascript

看个例子:html

arr[0];
// => "dazhi"
复制代码

这里的arr必定是一个数组吗?不必定,也多是一个对象。前端

let arr = {
    0: 'dazhi'
}
console.log(arr[0]); // dazhi
复制代码

再来看个例子:java

let arr = ['name', 'age', 'job'];

// 建立一个类数组对象
let arrLike = {
    0: 'name',
    1: 'age',
    2: 'job',
    length: 3
}
复制代码

注意:这边arrLike必须加上length属性,否则它就是一个普通对象而已。git

为何叫作类数组对象呢?咱们从读写、获取长度、遍历这三个方面来看看这两个对象。github

读写

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');  // 报错
复制代码

原形毕露了,终归是类数组。app

那若是类数组就想用数组的方法呢?能够把类数组转换为数组。函数

转换为数组

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属性

argumentslength属性表明的是实参的个数,由于咱们调用函数的时候,有时候并非全部参数都须要传入,看下面的代码:

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);
复制代码

总结:

  1. 有传入的参数,实参和arguments的值会共享,没有传入的参数,不会共享
  2. 在严格模式下,不管参数有没有传入,实参和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
复制代码

那么forEachfor...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的应用其实还有不少,这里就不继续展开了,你们有兴趣能够本身再去找资料学习一下,好比:

  1. 参数不定长
  2. 函数柯里化
  3. 递归调用
  4. 函数重载
  5. ...

其余类数组对象

咱们在页面上随便写几个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]
复制代码

好了,本文就先到这里了。

最后

感谢您的阅读,但愿对你有所帮助。因为本人水平有限,若是文中有描述不当的地方,烦请指正,很是感谢。

关注

欢迎你们关注个人公众号前端帮帮忙,一块儿交流学习,共同进步!

参考:

github.com/mqyqingfeng…

相关文章
相关标签/搜索