数组基于下标的类型和使用方式区分了索引数组和关联数组。
索引数组使用「数字类型」作为下标,按顺序访问元素。这种数组更像是一个线性表,内部元素有严格的顺序,通过数字索引可以直接存取。
关联数组使用「字符串类型、符号类型」作为下标,以「键-值」的形式存取元素。这种数组更像是一张表,内部元素没有严格顺序,通过字符串/符号作为键可以灵活存取。
索引数组追求顺序和索引访问,关联数组追求灵活存取方式。但两者本质上都是数组,只是在使用方法和存储方式上略有不同。
在JavaScript中,数组表现为索引数组,但具有对象的所有特性。
每个数组索引都是数组对象的自身属性。因此,默认情况下,数组可以像普通对象一样使用,包括对象展开和对象解构赋值。
大多数操作仍然基于对象的正常机制,但 JavaScript 额外处理了索引数组中的 length 属性。例如,在以下情况下会影响 length 属性:
-
使用\\”push\\”、\\”pop\\”、\\”shift\\”、\\”unshift\\”方法在数组的开头或末尾添加或删除元素时;
-
使用\\”splice\\”方法从数组中删除元素时;
-
当给大于或等于当前\\”length\\”值的索引赋值时,会隐式地重新设置\\”length\\”属性的值;
可以显式地重写\\”length\\”属性来调整数组的大小。
JavaScript 在处理索引数组时除了特殊处理了 length 属性之外,还提供了一些针对数组的特定方法和属性,并且保证了数组的迭代顺序和一些性能上的优化。这使得数组在处理有序集合数据时更加便捷和高效。
由于 JavaScript 数组是基于关联数组实现的,数组的很多方法都是使用递增或递减来列举元素:
JavaScript 数组的每个元素占据连续中一个位置,并且可以通过索引直接访问。数组的连续性使得通过索引来访问元素的时间复杂度为O(1),即具有常数级的访问速度。
JavaScript 引擎会尽力将数组存储为连续的内存空间,以提高访问性能。当数组需要扩展时,引擎会根据内存情况分配更大的连续内存块,并将原有元素复制到新的内存位置。这样可以确保数组的连续性,并且减少内存碎片化。
通常情况下,JavaScript 数组的元素存储在内存中,而不是硬盘上。只有在处理非常大的数组时,内存不足时,或者特定的I/O需求时,才会考虑将数组数据存储在硬盘等外部存储介质上。这种情况下,访问速度会受到硬盘读写速度的影响。
所以 JavaScript 数组是连续存储的,通过索引访问元素具有高效性。尽管在某些情况下,内存不足时可能使用硬盘存储,但这是相对较少见的情况。数组的连续性取决于当前的内存分配和 JavaScript 引擎的优化策略。
JavaScript的数组在存取特性上是即需即分配的动态数组,只有在向数组添加元素时才会分配相应的内存空间。这意味着在声明一个大型数组时,并不会导致实际的存储问题,不会立即分配内存空间来存储所有元素。
var aArray = new Array(100000);
for(var i=0; i<aArray.length; i++){
//列举 aArray.length 次
}
在上面的示例中,声明了一个包含 1,000,000 个元素的数组 aArray,但实际上没有任何元素被存入数组中。由于 JavaScript 的动态数组特性,这样的声明并不会导致分配 1,000,000 个元素所需的大量内存空间。
对于这样的空数组进行遍历操作,即使没有实际元素需要处理,仍然会导致遍历代码的执行次数为 aArray.length,即 1,000,000 次。由于遍历是基于数组长度进行的,仍然会产生一定的虚耗,因为需要执行大量的循环迭代操作。
虽然遍历空数组不会带来实际的存储问题,但对于性能而言,无意义的遍历操作可能会浪费计算资源。
在 V8 引擎中,有一些额外的策略,这个策略被称为「ElementsKind」转换,它是为了减少频繁的内存分配和数据迁移操作。
具体来说就是,V8 引擎在处理数组时,会根据数组的大小和元素的类型选择一个称为 ElementsKind 的内部表达方式,这个内部表达方式可以容纳一定数量的元素,通常是当前数组大小的 1.5 倍左右。
当数组需要扩展时,引擎会检查当前数组数量是否已经接近容量上限,如果是,则会分配一个更大的内部空间,将原有元素都迁移到新的空间中,继续添加元素。
对于大型数组或者需要频繁搜索的情况,使用常见的 indexOf() 方法并不会带来高效的搜索效果,这是因为 indexOf() 方法需要逐个遍历数组元素,直到找到匹配的元素或搜索到数组末尾。
当数组进一步无序或自由存取的时候(下标空洞),列举数组中的元素就进行搜索的效率可能会更差,这是因为无序数组结构无法使用「二分查找」等高效的搜索算法。
所以对于大型数组或者需要频繁搜索的情况,可以考虑其他的数据结构或算法来提高搜索效率,例如使用哈希表、排序后再进行搜索,或者将数据结构改造为更适合搜索操作的形式。
题图授权:Midjourney
内容优化:ChatGPT
内容来源:《JavaScript 语言精髓与编程实战》
原创文章,作者:小道研究,如若转载,请注明出处:https://www.sudun.com/ask/34602.html