数组和类数组

4,758 阅读4分钟

前言

我们经常会听人提到类数组,也经常会遇到类数组转真数组的怎么转的问题?要理解类数组,肯定会谈起数组,那我们今天就来探究探究

我的简单理解

我的理解是,js数组是用一个变量存储多个数据的一种特殊的数据结构,可以通过数组下标获取对应位置的数据,并且js提供了一系列的属性和方法来操作数组。

而类数组其实就是类似数组的对象;本质是长得很像的两个东西 一个对象一个数组,因为它长得像数组,使用起来也挺像数组,所以大家后面习惯把它叫做类数组。

类数组的组成

类数组有几个必要组成部分:

  1. 属性要为索引(数字)属性;
  2. 必须有length属性
  3. 最好加上数组的push和splice方法

类数组并不陌生,函数中的arguments就是属于类数组,在DOM操作中,我们也经常会得到一个类数组(比如document.getElementsByClassName())

那再来深入看看,到底里面都有什么?

深入探究

先定义一个普通方法并执行,把打印 arguments看看

function fn(){
  console.log(arguments)
}
fn('a','b','c')

结果如下:

image.png

这验证了我们上面说的两点:

  1. 属性要为索引属性;
  2. 必须有length属性

接下来继续试试,看类数组能不能像数组那样进行一些操作

arguments[0] = '通过下标修改已有数据';
arguments[3] =  "通过下标添加新的数据"
console.log(arguments)
console.log(arguments.length)//3

结果如下

image.png

发现我们通过下标的方式修改、查看和添加类数组的数据,但是这种方式不会影响length。仔细一想,这更像对象的基本特性,就是将下标当他属性来存取。只是他看起来像数组一样。

接着看看能不能用数组的push方法

arguments.push("push");

答案跟你想的一样,是不可以的!

它会一个错误:Uncaught TypeError: arguments.push is not a function

其实你只要想清楚一个事就很容易理解了, 我问问大家,我们在数组中使用的一些列方法(如push、pop、shift、splice、join、concat...等等)都是定义在哪里的?是不是定义在构造函数Array的原型上的,这样所以的实例数组都能使用!而这里arguments即没有添加对应的方法,也没有继承自Array.prototpe,自然没有这写数组方法。

接下来我们来给arguments添加一个push方法

function fn() {
  arguments.push = Array.prototype.push;
  arguments.push('d');
  console.log(arguments)
}
fn('a', 'b', 'c')

结果:添加push方法的类数组,push数据的时候会动态的增长length属性。

image.png

除了arguments我们可以自定义一个类数组,如下

//自定义数组
var obj = {
  1: "a",
  2: 'b',
  3: 'c',
  length: 3,
  push: Array.prototype.push,
  splice: Array.prototype.splice
}

有人会问,为什么这里增加splice方法?

其实在最开始的时候有说到,类数组必要的几个部分中,最后一条:最好加上push和splice。push可以在push数据的同时动态的增长length属性,而splice呢?在某些教程中讲到,可以让类数组打印出来时看上去跟数组一模一样。不过我目前的最新谷歌版本是打印出来还是类数组的样子:

image.png

不过这样不要紧,splice加不加都行。

这里插播一个笔试题吧:

var obj = {
  2: 'a',
  3: 'b',
  length : 2,
  push: Array.prototype.push,
}
obj.push('c');
obj.push('d');
console.log(obj);

答案可能出乎你得预料:

image.png

其实的理解push的内部原理,得回到我们之前手动封装push方法的时候

Array.prototype._push = function(){
    for(var i=0;i<arguments.length;i++){
        this[this.length] = arguments[i];
        this.length++
    }
    return this.length;
}

里头核心操作this[this.length] = arguments[i]this.length++,就是跟数组的length位置添加数据,然后让length++。所以有了上面的答案。

最后,如果你希望将数组转成真数组,那继续往下看:

类数组转成真数组的几种方法

Array.prototype.slice.call(obj)

在讲数组的时候有提到,通过slice方法可以拷贝得到新数组

var arr = [1,2,3,4,5];
var newArr = arr.slice()
console.log(newArr);//截取整个数组       [1,2,3,4,5];

Array.from()方法

var arr= Array.from(obj);

用ES6的扩展运算符 ...

var arr= [...obj];

以上是最简单也最常见的3种方式

END

以上是关于数组和类数组相关内容

如有问题欢迎留言告知~