JS数组不是“真”的数组?

数组是我们前端日常开发中最熟悉的一种数据类型,但你真的了解数组吗?

什么是数组?

数组的定义:

数组(array)是一种线性表数据结构,它用一组连续的内存空间来存储一组具有相同类型的数据

首先它是一个线性表

  • 线性表:就是数据排列成一条先一样的结构,每个线性表上的数据最多只有前和后两个方向。除了数组,链表,队列,栈等也是线性表结构。

  • 非线性表:二叉树,堆,图等。之所以是非线性,是因为数据之间并不是简单的前后关系。

  • 然后它是连续的内存空间相同的数据类型

有了这两个限制,才能让数组可以随机访问。当然有利就有弊,这两个限制也让数组的很多操作变得低效,比如删除和插入操作,为了保证连续性需要做大量的数据搬移工作。

数组是如何根据下标实现随机访问的?

比如一个长度为10的int类型的数组int[] a = new int[10],

image.png

image.png

从图中可以看到,计算机给数组a分配了一块连续的内存1000-1039.其中内存块的首地址为base_address=1000。

我们知道计算机会给每一个内存单元分配一个地址,计算机通过地址来访问内存中的数据。当计算机要访问数组中的某个元素的时候就会通过下面公式寻找地址

a[i]_address = base_address + i*data_type_size
这里的data_type_size表示数组中每个元素的大小,因为例子中是int所以大小为4.

注意,这里有两个关键词:相同类型连续内存,这也是它的特征!好,重点来了:

那我怎么及得JS中的数组元素可以是各种类型???比如下面这样:

let arr = [100, 'foo', 1.1, {a: 1}];

这就有意思了,按理维基百科对于数组的描述应该是具有一定权威的,难道JS的数组不是真的“数组”?

image

image

这么来看,我们姑且推断出一个结论,因为:不同数据类型存储所需空间大小不同。
所以:用来存放数组的内存地址一定是连续的(除非类型相同)
因此我们大胆猜测,JS中的数组实现一定不是基础的数据结构实现的。所以,如标题所说的,JS中原本没有“真正”的数组!这就引起了我的好奇心了,那么JS中是如何“实现”数组这个概念的呢? 我们来一探究竟!

数组中概念一:连续内存

在讲连续内存前,先来了解下什么是内存,知道的本节直接绕过。

  1. 什么是内存?
    通俗理解,在计算机中,CPU用于数据的运算,而数据来源于硬盘,但考虑到CPU直接从硬盘读写数据效率低,所以内存在其中扮演了“搬运工”的角色。

内存是由DRAM(动态随机存储器)芯片组成的。DRAM的内部结构可以说是PC芯片中最简单的,是由许多重复的“单元”——cell组成,每一个cell由一个电容和一个晶体管(一般是N沟道MOSFET)构成,电容可储存1bit数据量,充放电后电荷的多少(电势高低)分别对应二进制数据0和1。

DRAM由于结构简单,可以做到面积很小,存储容量很大。用芯片短暂存储数据,读写的效率要远高于磁盘。所以内存的运行也决定了计算机的稳定运行。

  1. 内存和数组的故事
    了解完什么是内存后,回过头再来看一下数组的概念:

数组是由相同类型的元素(element)的集合所组成的数据结构,分配一块连续内存来存储。

那么数组中的连续内存说的是,通过在内存中划出一串连续且长度固定的空间,用来于存放一组有限且数据类型相同的数据结构。在C/C++、Java等编译型语言中数组的实现都是这个。C++数组声明示例代码如下:

// 数据类型 数组名[元素数量]
double balance[10];

上述代码中,需要指定元素类型和数量,这非常重要。细细品味其中的蕴含的内容,将其联系到内存以及计算机运行原理信息量很大!

数组中概念二:固定长度

从上面说的就很容易理解,计算机语言设计者为什么要让C/C++、Java这类编译型语言在数组的设计上要固定长度。因为数组空间数连续的,所以这就意味着内存中需要有一整块的空间用来存放数组。如果长度不固定,那么内存中位于数组之后的区域没法继续往下分配了!内存不知道当前数组还要不要继续存放,要多少空间了。毕竟数组后面的空间得要留出来给别人去用,不可能让你(数组)一直霸占着对吧。

数组中概念三:相同数据类型

为什么数组的定义需要每个元素数据类型相同呢。其实比较容易理解了,如果数组允许各种类型的数据,那么每存入一个元素都要进行装箱操作,每读取一个元素又要进行拆箱操作。统一数据类型就可以省略装箱和拆箱的步骤了,这样能提高存储和读取的效率。

数组优缺点

优点:

  1. 按照索引查询元素速度快
  2. 能存储大量数据
  3. 按照索引遍历数组方便

缺点:

  1. 根据内容查找元素速度慢
  2. 数组的大小一经确定不能改变。
  3. 数组只能存储一种类型的数据
  4. 增加、删除元素效率慢
  5. 未封装任何方法,所有操作都需要用户自己定义。

为什么数组的插入和删除是低效的?

插入操作:

假如数组的长度为n,如果我们需要将一个数据插入到数组中的第k个位置,为了把第k个位置腾出来,需要把第k~n这部分的元素都顺序的往后挪一位,操作复杂。

如果在数组的末尾插入元素,那就不需要移动数据了,插入比较简单,单是如果在数组的头部插入元素就复杂了,需要把所有的数据都往后移动一位。

如果一个数组中的数据是有序的,那我们在某一个位置插入一个新的元素的时候就必须得按照上面的方法移动k之后的数据,但是数组中存储的数据如果没有任何规律,这时候,如果想要把某个元素插入到k位置,为了避免大规模的数据搬移,有个简单的办法就是:直接把k位置的数据搬到数组的最后面,把新元素放到第k个位置。

删除操作:

跟插入数据类似,如果我们要删除第k个位置的数据,为了内存的连续性,也需要搬移数据,不然中间就会出现空洞。

在某些情况下,如果我们不要求数据必须是连续的,那么删除的时候可以不真删除,只是把这个元素标记为已删除,当组空间不够用的时候,在触发一次真正的删除,这样就大大减少了删除操作导致的搬移操作。

 朵拉姐【ITI2018】的前端摸鱼技术群

欢迎大家技术交流 内推 摸鱼 求助皆可 - 链接

系列链接(后续都会更新完毕)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值