JavaScript稀疏数组示例教程

目录
  • 前言
  • 什么是稀疏数组?
  • 创建带有孔洞的稀疏数组
  • 删除元素的映射
  • 现象
  • 稀疏数组的快速映射(强制创建映射关系)
  • 总结

前言

最近有空在看一本关于 JS 数据结构和算法的书,里面有提到数组,却对数组的基本概念轻轻带过,虽然用了 JS 很久但是一直忙于需求业务的实现从未停下好好回视一下这个 既熟悉又陌生的朋友,于是查阅了一些资料,尤其是密集数组和稀疏数组的区别,意犹未尽之下,写了这篇文章,以便更好地帮助理解书中的要点,稍显浅显,也有不足望各位提点。

什么是稀疏数组?

通常编程语言中(C、JAVA等)数组都是预先设定好长度的,他们的内存占用是固定的,内存地址是连续不间断、紧密相连的,我们称之为密集数组,好比 JS 中类型化数组(TypedArray)的 ArrayBuffer,但我们这里着重要说的是通过 Array 创建的数组,它其实是个对象,当我们通过 new Array() 创建一个数组时,它只是一个带有 length 属性的数组对象,我们可以像对象一样去操作它的属性:

let array = new Array(10)
// 下标可以是任何的字符串,就像操作 Object 一样
array.name = 'This is an Array.'
array['name'] = 'This is an Array.' // 等效 array.name

虽然可以像操作 Object 那样为数组对象添加属性,但是真正计入数组元素的只能是以整数类型的字符作为下标去映射值的元素,同时会影响其长度属性 length,我们接上段代码继续:

// 虽然之前赋上了一个新的属性 name,但是其长度仍然输出10,而非11
console.log(array.length) // 10
// 用整数类型下标定义一个数组元素才会计入数组长度
array['0'] = 'first' // 下标其实都是字符串
array[11] = 'eleventh' // 看起来这个下标是一个数字,但是 JS 会自动把它转为字符
console.log(array.length) // 12

长度属性是可以手动变更的:

array.length = 100
console.log(array.length) // 100

数组对象本身可能不占用太多内存,它只是包含了映射关系和长度,真正占用内存的是元素所映射的目标,也就是“键值”中的“值”。在V8引擎里,JS 的数组得到进一步的优化,其中有个概念叫 Holey(有孔洞的),也就是那些没有被指明映射关系的空间,他们不占用内存。如上看到,作为一门动态语言,我们随时可以向这个数组对象里添加、修改元素映射,也能随时改变其长度 length。自然地,其元素所占的内存地址也就无所谓固定连续了,我们称这种数组为稀疏数组(Sparse Array)。

创建带有孔洞的稀疏数组

  • 使用 Array 构造函数:
let sparse = new Array(100)
  • 通过字面量:
let sparse = []
// 用一个超过原始长度的下标为元素赋值,也就为该元素创建了一个映射关系,同时改变了该数组的长度
sparse[99] = 'last'
console.log(sparse.length) // 100
// 用对象字面量书写数组时,允许元素留空
sparse = [, , ,] // 这就创建了长度为3的空洞的数组
console.log(sparse.length) // 3
  • 手动修改一个大于原数组长度的 length 值:
let sparse = ['red', 'green', 'blue'] // 三个元素的数组
sparse.length = 100 // 此时长度已是100,但有效元素仍然只有3个

删除元素的映射

从数组中删除某个元素可以使用 pop、shift、splice 方法,那如何解除元素与某个对象的映射关系呢?我们可以像操作对象一样用 delete,它不会删除映射目标,仅仅是将元素和目标对象的关联断开,从而形成一个孔洞,所以也不会改变这个数组的 length。

let one = '壹'
let array = []
array[0] = one // 将数组的第一个元素指到对象 one,长度变为 1
console.log(array, array.length) // ['壹'], 1
delete array[0] // 删除数组第一个元素的映射关系
console.log(array) // [空]
console.log(array.length) // 因为只是删除了元素的映射,长度并没有改变,仍然输出 1
console.log(one) // one 的值并不会删除,仍然保留,输出 '壹'

现象

我们说到,JS 的数组本质上是对象——由整数字符作为下标与目标值构成映射关系的自带长度属性的对象,长度可以大于有效(已创建映射关系的)元素的个数。从映射状态来看,完全映射的数组有着和传统密集数组类似的表现,而不完全映射的数组在生产中会有些特别,但又在情理之中。

  • 访问没有映射关系的数组元素时,相当于一个申明了却没有定义值的变量,所以会输出 undefined:
let sparse = new Array(10)
console.log(sparse[0]) // undefined
  • 不完全映射的数组,在用 map()、forEach() 等方法做遍历时,它们只会遍历已有映射关系的元素:
// 此时数组元素的映射关系一个都没有创建,所以 forEach 不会有任何输出
sparse.forEach((value, i) => {
  console.log(i, value)
}) // nothing
// 根据下标为最后一个元素赋值
sparse[sparse.length - 1] = 'tenth'
// 只会输出已建立映射的元素 sparse[sparse.length-1] 的值 'tenth'
sparse.forEach((value, i) => {
  console.log(i, value)
}) // 9, tenth
  • 使用 for 语句也只是用数组的 length 值作为循环依据,它不会主动判断当前位置是否有映射值,所以当循环体试图通过下标访问没有映射关系的位置时,会输出 undefined:
for( let i = 0; i < sparse.length; i += 1){
  console.log(i, sparse[i])
}
  • 映射完全的数组,可以被所有常用数组方法遍历:
let array = ['1', 'day', 'white', 'Jake', undefined, null, 0] // 一个完全映射的数组
// 输出每个元素,包括null、undefined、0
array.forEach((value, i) => {
  console.log(i, value)
})
  • 在做 some()、every() 等操作时,不完全映射的数组的表现是特殊的,但也在情理之中,这取决于这些方法的设计,例如 some(),即便长度大于0,但因为其中没有任何建立映射的元素,所以,相当于给一个空数组([])做操作:
let sparse = new Array(10)
// 由于还没有建立映射关系,所以 some 的回调也没有触发,于是得到的结果依然是 false
console.log(sparse.some(item => !item)) // false
// 在所有元素都被赋值后,用同样的回调,some 的结果发生了变化
sparse.fill(false) // 赋值
console.log(sparse.some(item => !item)) // true
  • 当把长度设成小于实际元素个数的值时,会把超出长度的元素从当前数组中剔除:
let array = ['one', 'two', 'three']
array.length = 1
console.log(array) // ['one']

稀疏数组的快速映射(强制创建映射关系)

只是让数组中的映射元素个数与长度属性相同,并不能改变其稀疏的特性:

let sparse = new Array(5)
// Array.apply
let array1 = Array.apply(null, sparse)
// Array.from方法
let array2 = Array.from(sparse)
// 解构
let array3 = new Array(...sparse)
let array4 = [...sparse]
// 把所有元素映射为 undefined 了
array1.forEach((item, i) => {
  console.log(i, item) // 输出 undefined × 5
})
……

总结

以上是对 JS Array 数组的粗浅认知,在 JS 这门动态语言里,通过 Array 所创建的数组无所谓疏密,因为它们本质上还是对象。在实际生产时,对于数据集合的操作应当尽量保持映射完全,避免不合预期的意外,更多关于JavaScript稀疏数组教程的资料请关注我们其它相关文章!

(0)

相关推荐

  • javascript稀疏数组(sparse array)和密集数组用法分析

    本文实例讲述了javascript稀疏数组(sparse array)和密集数组用法.分享给大家供大家参考,具体如下: 学习underscore.js数组相关API的时候,遇到了sparse array这个东西,以前没有接触过. 这里学习下什么是稀疏数组和密集数组. 什么是密集数组呢?在Java和C语言中,数组是一片连续的存储空间,有着固定的长度.加入数组其实位置是address,长度为n,那么占用的存储空间是address[0],address[1],address[2].......addr

  • JavaScript中利用Array filter() 方法压缩稀疏数组

    什么是稀疏数组 数组元素的索引不一定要连续的,它们之间可以有空缺.每个javaScript数组都有一个length属性.针对非稀疏数组,该属性就是数组元素的个数:针对稀疏数组,length比所有元素的个数要大. Array filter() 方法会跳过稀疏数组中缺少的元素,它的返回数组总是稠密的. (1)压缩稀疏数组的空缺: var dense = sparse.filter( function(currentValue) { return true; } ); (2)压缩稀疏数组的空缺,并且删

  • js判断两个数组相等的5种方法实例

    目录 前言 一. toString() 二.join() 三. JSON.stringify() 四.sort() 五.filter() 总结 前言 在 js 中是不能直接用 == 或者 === 来比较两个数组是否相等,那就需要对数组的值进行比较. 下面各种方法,要根据具体情况来使用. 一. toString() 当两个数组元素类型相同,顺序相同时,直接判断是否相等,结果不相等:转化为字符串后,结果相等 [1,2,3].toString() === [1, 2, 3].toString(); /

  • JS实现数组随机排序的三种方法详解

    目录 1.利用数组方法sort实现随机排序 2.洗牌算法实现随机排序 3.洗牌算法深入分析 全部代码 1.利用数组方法sort实现随机排序 实现随机排序方法还是很多的,用for循环是可以写的,用Lodash等三方js方法库也行.但个人以为使用sort比较方便,但是他又缺点,缺点就是不够那么的随机,我看过sort运行机制后,发现他竟然是利用一个比较器两两比较出来的. var arr = [1, 2, 3, 4, 5] arr.sort(function () { return Math.rando

  • JavaScript中的稀疏数组与密集数组[译]

    1.稀疏数组 创建一个指定长度的稀疏数组很简单: 复制代码 代码如下: > var a = new Array(3); > a [ , , ] > a.length 3 > a[0] undefined 当你遍历它时,你会发现,它并没有元素.JavaScript会跳过这些缝隙. 复制代码 代码如下: > a.forEach(function (x, i) { console.log(i+". "+x) }); > a.map(function (x,

  • JavaScript稀疏数组示例教程

    目录 前言 什么是稀疏数组? 创建带有孔洞的稀疏数组 删除元素的映射 现象 稀疏数组的快速映射(强制创建映射关系) 总结 前言 最近有空在看一本关于 JS 数据结构和算法的书,里面有提到数组,却对数组的基本概念轻轻带过,虽然用了 JS 很久但是一直忙于需求业务的实现从未停下好好回视一下这个 既熟悉又陌生的朋友,于是查阅了一些资料,尤其是密集数组和稀疏数组的区别,意犹未尽之下,写了这篇文章,以便更好地帮助理解书中的要点,稍显浅显,也有不足望各位提点. 什么是稀疏数组? 通常编程语言中(C.JAVA

  • JavaScript稀疏数组与孔hole示例详解

    目录 稀疏数组是什么 JavaScript数组天生就是稀疏数组 JavaScript数组稀疏特性带来的“怪异现象” slice会复制孔 forEach.every会跳过孔(不对孔调用回调函数) map不对孔调用回调函数,但是孔会保留 filter不对孔调用回调函数,但是孔会被过滤掉 join会将孔转化为一个空字符串进行拼接,与undefined一样 初始化无孔数组的方法 Array.apply(null, Array(n))的原理 稀疏数组是什么 在绝大多数JavaScript的实现中,数组是稀

  • javascript数据类型基础示例教程

    js中的输入输出语句 方法 说明 归属 alert(msg) 浏览器弹出警示框 浏览器 console.log(msg) 浏览器控制台打印输出信息 浏览器 prompt(info) 浏览器弹出输入框,用户可以输入 浏览器 注:prompt(info)获取的输入都是字符串类型,跟python中的input()类似. <script> prompt("请输入:"); alert('你好呀!小可爱!'); console.log('阿牛最帅啦!'); </script>

  • java数据结构算法稀疏数组示例详解

    目录 一.什么是稀疏数组 二.场景用法 1.二维数组转稀疏数组思路 2.稀疏数组转二维数组思路 3.代码实现 一.什么是稀疏数组 当一个数组a中大部分元素为0,或者为同一个值,那么可以用稀疏数组b来保存数组a. 首先,稀疏数组是一个数组,然后以一种特定的方式来保存上述的数组a,具体处理方法: 记录数组a一共有几行几列 记录a中有多少个不同的值 最后记录不同值的元素所在行列,以及具体的值,放在一个小规模的数组里,以缩小程序的规模. 这个小规模的数组,就是稀疏数组. 举个栗子,左侧是一个二维数组,一

  • javascript基础数据类型转换教程示例

    目录 数值型转换为字符串类型 字符串类型转换为数值型 转换为布尔型 结语 数值型转换为字符串类型 方式 说明 案例 toString() 转成字符串 var num =1; alert ( num.toString()); String() 强制转换 转成字符串 var num =1; alert ( String ( num )); 加号拼接字符串 和字符串拼接的结果都是字符串 var num =1; alert ( num +"我是字符串"); // 1.将数字型转换为字符串类型 var n

  • 详解JavaScript中数组的相关知识

    创建数组 js中数组的声明可以有如下几种方式: var arr = []; // 简写模式 var arr = new Array(); // new一个array对象 var arr = new Array(arrayLength); // new一个确定长度的array对象 要说明的是: 虽然第三种方法声明了数组的长度,但是实际上数组长度是可变的.也就是说,即使指定了长度为5,仍然可以将元素存储在规定长度之外,这时数组的长度也会随之改变. 此外,还需要明确的一点: js是弱类型语言,也就是数

  • JavaScript遍历数组和对象的元素简单操作示例

    本文实例讲述了JavaScript遍历数组和对象的元素.分享给大家供大家参考,具体如下: 博主最近一直在学习JavaScript,今天给大家分享如何遍历JavaScript中的数组和对象的元素,这篇文章相当于做一篇小的笔记,希望能帮助到大家! 遍历数组 <script> var arr = ['关羽','张飞','赵云','马超','黄忠'];//定义数组 //遍历数组 for(var i = 0;i<arr.length;i++){ console.log(arr[i]); } <

随机推荐