数组结构

  • 几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构。
  • 数组通常情况下用于存储一系列同一种数据类型的值。
  • 但在JavaScript里,也可以在数组中保存不同类型的值。
  • 但我们还是要遵守最佳实践,别这么做(大多数语言都没这个能力)。

补充普通语言的数组封装(比如Java的ArrayList)

  • 常见语言的数组不能存放不同的数据类型,因此所有在封装时通常存放在数组中的是Object类型
  • 常见语言的数组容量不会自动改变.(需要进行扩容操作)
  • 常见语言的数组进行中间插入和删除操作性能比较低

一.数组的基本使用

  • 几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构。
  • 数组通常情况下用于存储一系列同一种数据类型的值。
  • 在JavaScript里,也可以在数组中保存不同类型的值。

为什么使用数组?

100个变量的是非常不方便管理的, 而且当我们需要找到某一个时, 从100个变量中去搜索也是一个问题.

创建和初始化数组

用JavaScript声明、创建和初始化数组很简单,就像下面这样:

// 创建和初始化数组
var daysOfWeek = new Array()
var daysOfWeek = new Array(7)
var daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday')
  • 使用new关键字,就能简单地声明并初始化一个数组
  • 用这种方式,还可以创建一个指定长度的数组.
  • 另外,也可以直接将数组元素作为参数传递给它的构造器
  • 用new创建数组并不是最好的方式。如果你想在JavaScript中创建一个数组,只用中括号([])的形式就行了

数组长度和遍历数组

  • 如果我们希望获取数组的长度, 有一个length属性
// 获取数组的长度
alert(daysOfWeek.length)
  • 也可以通过下标值来遍历数组:
// 普通for方式遍历数组
for (var i = 0; i < daysOfWeek.length; i++) {
alert(daysOfWeek[i])
} // 通过foreach遍历数组
daysOfWeek.forEach(function (value) {
alert(value)
})

二. 数组的常见操作

数组中常见的操作有: 添加元素、删除元素、修改元素、获取元素.

添加元素

  • JavaScript中, 进行我们上述的操作都比较简单: 因为语言本身都已经封装好了这些特定.
  • 添加一个元素到数组的最后位置:
// 添加一个元素到数组的最后位置
// 方式一:
numbers[numbers.length] = 10 // 方式二:
numbers.push(11)
numbers.push(12, 13) alert(numbers)
  • 在数组首位插入一个元素:
// 在数组首位插入一个元素
for (var i = numbers.length; i > 0; i--) {
numbers[i] = numbers[i-1]
}
numbers[0] = -1
alert(numbers) // -1,0,1,2,3,4,5,6,7,8,9,10,11,12,13
  • 考虑上面代码实现的性能怎么呢?

    • 性能并不算非常高
    • 这也是数组和链表(后面我们会学习到)相对比的一个劣势: 在中间位置插入元素的效率比链表低.
  • 我们在数组首位插入数据可以直接使用unshift方法
// 通过unshift在首位插入数据
numbers.unshift(-2)
numbers.unshift(-4, -3)
alert(numbers) // -4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13

删除元素

  • 如果希望删除数组最后的元素, 可以使用pop()方法
// 删除最后的元素
numbers.pop()
alert(numbers) // -4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12
  • 如果我们希望移除的首位元素, 自己实现代码:
// 删除首位的元素
for (var i = 0; i < numbers.length; i++) {
numbers[i] = numbers[i+1]
}
numbers.pop()
alert(numbers)
  • 我们可以直接使用shift方法来实现:
numbers.shift()
alert(numbers)

任意位置

  • 任意位置?

    • 前面我们学习的主要是在数组的开头和结尾处添加和删除数据.
    • 那如果我们希望在数组的中间位置进行一些操作应该怎么办呢?
  • 一方面, 我们可以自己封装这样的函数, 但JS已经给我们提供了一个splice方法
  • 通过splice删除数据
// 删除指定位置的几个元素
numbers.splice(5, 3)
alert(numbers) // -4,-3,-2,-1,0,4,5,6,7,8,9,10,11,12,13
  • 代码解析:

    • 上面的代码会删除索引为5, 6, 7位置的元素.
    • 第一个参数表示索引起始的位置为5(其实是第6个元素, 因为索引从0开始的), 删除3个元素.
  • 如果我们希望使用splice来插入数据呢?
// 修改指定位置的元素
numbers.splice(5, 3, "a", "b", "c")
alert(numbers) // -4,-3,-2,-1,0,a,b,c,4,5,6,7,8,9,10,11,12,13
  • 代码解析:

    • 上面的代码会从索引5的位置开始修改数据, 修改多少个呢? 第二个参数来决定的.
    • 第一个参数依然是索引的位置为5(第六个位置)
    • 第二个参数是要将数组中多少个元素给替换掉, 我们这里是3个(也可以使用3个元素来替换2个, 可以自己尝试一下)
    • 后面跟着的就是要替换的元素.

三. 数组的其他操作

上面学习的是对数组的一些基本操作.

JavaScript中添加了很多方便操作数据的方法, 我们一些来简单回顾一下.

常见方法

  • 我们先对常见的方法简单来看一下

数组合并

  • 数组的合并非常简单, 使用concat即可(也可以直接+进行合并)
// 数组的合并
var nums1 = [1, 2, 3]
var nums2 = [100, 200, 300]
var newNums = nums1.concat(nums2)
alert(newNums) // 1,2,3,100,200,300 newNums = nums1 + nums2
alert(newNums) // 1,2,3,100,200,300

迭代方法

  • 为了方便操作数组, JS提供了很多迭代器方法, 我们来回顾一下
  • every()方法
    • every()方法是将数组中每一个元素传入到一个函数中, 该函数返回true/false.
    • 如果函数中每一个元素都返回true, 那么结果为true, 有一个为false, 那么结果为false
  • every()练习:
  • 判断一组元素中是否都包含某一个字符
// 定义数组
var names = ["abc", "cb", "mba", "dna"] // 判断数组的元素是否都包含a字符
var flag = names.every(function (t) {
return t.indexOf('a') != -1
})
alert(flag)
  • some()方法

    • some()方法是将数组中每一个元素传入到一个函数中, 该函数返回true/false
    • 但是和every不同的是, 一旦有一次函数返回了true, 那么迭代就会结束. 并且结果为true
  • some()练习

// 定义数组
var names = ["abc", "cb", "mba", "dna"] // 判断数组中是否包含有a字符的字符
var flag = names.some(function (t) {
alert(t)
return t.indexOf("a") != -1
})
alert(flag)
  • forEach()方法

    • forEach()方法仅仅是一种快速迭代数组的方式而已.
    • 该方法不需要返回值
  • forEach的使用
// 定义数组
var names = ["abc", "cb", "mba", "dna"] // forEach的使用
names.forEach(function (t) {
alert(t)
})
  • filter()方法

    • filter()方法是一种过滤的函数
    • 首先会遍历数组中每一个元素传入到函数中
    • 函数的结果返回true, 那么这个元素会被添加到最新的数组中, 返回false, 则忽略该元素.
    • 最终会形成一个新的数组, 该数组就是filter()方法的返回值
  • filter()的练习:
// 定义数组
var names = ["abc", "cb", "mba", "dna"] // 获取names中所有包含'a'字符的元素
var newNames = names.filter(function (t) {
return t.indexOf("a") != -1
})
alert(newNames)
  • map()方法

    • map()方法提供的是一种映射函数.
    • 首先会遍历数组中每一个元素传入到函数中.
    • 元素会经过函数中的指令进行各种变换, 生成新的元素, 并且将新的元素返回.
    • 最终会将返回的所有元素形成一个新的数组, 该数组就是map()方法的返回值
  • map()练习:
// 定义数组
var names = ["abc", "cb", "mba", "dna"] // 在names中所有的元素后面拼接-abc
var newNames = names.map(function (t) {
return t + "-abc"
})
alert(newNames)
  • reduce方法

    • 我们单独拿出reduce方法, 因为这个方法相对来说难理解一点
    • 首先, 我们来看这个方法需要的参数:
arr.reduce(callback[, initialValue])
  • 参数

    • callback(一个在数组中每一项上调用的函数,接受四个函数:)

      - previousValue(上一次调用回调函数时的返回值,或者初始值)

      - currentValue(当前正在处理的数组元素)

      - currentIndex(当前正在处理的数组元素下标)

      - array(调用reduce()方法的数组)

      • initialValue(可选的初始值。作为第一次调用回调函数时传给previousValue的值)
  • 理解
    • 求一个数字中数字的累加和
  • 使用for实现:
// 1.定义数组
var numbers = [1, 2, 3, 4] // 2.for实现累加
var total = 0
for (var i = 0; i < numbers.length; i++) {
total += numbers[i]
}
alert(total) // 10
  • 使用forEach简化for循环

    • 相对于for循环, forEach更符合我们的思维(遍历数组中的元素)
// 3.使用forEach
var total = 0
numbers.forEach(function (t) {
total += t
})
alert(total)
  • 使用reduce方法实现
// 4.使用reduce方法
var total = numbers.reduce(function (pre, cur) {
return pre + cur
})
alert(total)
  • 代码解析:

    • pre中每次传入的参数是不固定的, 而是上次执行函数时的结果保存在了pre中
    • 第一次执行时, pre为0, cur为1
    • 第二次执行时, pre为1 (0+1, 上次函数执行的结果), cur为2
    • 第三次执行时, pre为3 (1+2, 上次函数执行的结果), cur为3
    • 第四次执行时, pre为6 (3+3, 上次函数执行的结果), cur为4
    • 当cur为4时, 数组中的元素遍历完了, 就直接将第四次的结果, 作为reduce函数的返回值进行返回.
  • 似乎和forEach比较没有太大的优势呢?

    • 通过这个代码你会发现, 你不需要在调用函数前先定义一个变量, 只需要一个变量来接收方法最终的参数即可.
    • 但是这就是优势吗? 不是, 优势在于reduce方法有返回值, 而forEach没有.
    • 这算什么优势? 如果reduce方法有返回值, 那么reduce方法本身就可以作为参数直接传递给另外一个需要reduce返回值的作为参数的函数. 而forEach中你只能先将每次函数的结果保存在一个变量, 最后再将变量传入到参数中.
    • 没错, 这就是最近非常流行的函数式编程. 也是为了几乎每个可以使用函数式编程的语言都有reduce这个方法的原因.
    • 关于函数式编程, 不再本次课程的讨论之中, 只是看到了这个函数, 给大家延伸了一下而已.(后面有机会和大家分享函数式编程)
  • initialValue还需要讲吗?

    • 其实就是第一次执行reduce中的函数时, pre的值.
    • 因为默认pre第一次执行时为0.

JS数据结构与算法-数组结构的更多相关文章

  1. js数据结构与算法存储结构

    数据结构(程序设计=数据结构+算法) 数据结构就是关系,没错,就是数据元素相互之间存在的一种或多种特定关系的集合. 传统上,我们把数据结构分为逻辑结构和物理结构. 逻辑结构:是指数据对象中数据元素之间 ...

  2. JS数据结构与算法-队列结构

    队列结构 一.认识队列 受限的线性结构: 我们已经学习了一种受限的线性结构:栈结构. 并且已经知道这种受限的数据结构对于解决某些特定问题,会有特别的 效果. 下面,我们再来学习另外一个受限的数据结构: ...

  3. JS数据结构与算法-栈结构

    一.认识栈结构 栈也是一种非常常见的数据结构,并且在程序中的应用非常广泛 数组 我们知道数组是一种线性结构,并且可以在数组的任意位置插入和删除数据. 但是有时候,我们为了实现某些功能,必须对这种任意性 ...

  4. JS数据结构与算法——栈

    JS数据结构与算法--栈 1.栈结构概念 栈(Stack)是一种先进后出(LIFO Last in First out)的线性表,先进栈的将会比后进栈的先出栈. 栈的限制是仅允许在一端进行插入和删除运 ...

  5. JS数据结构与算法-概述

    JS数据结构与算法概述 数据结构: 计算机存储, 组织数据的方式, 就像锅碗瓢盆 算法: 一系列解决问题的清晰指令, 就像食谱 两者关系: 程序 = 数据结构 + 算法 邂逅数据结构与算法 什么是数据 ...

  6. JavaScript数据结构与算法-数组练习

    一. 创建一个记录学生成绩的对象,提供一个添加成绩的方法,以及一个显示学生平均成绩的方法. // 创建一个记录学生成绩的对象 const Students = function Students () ...

  7. Java数据结构和算法 - 数组

    Q: 数组的创建? A: Java中有两种数据类型,基本类型和对象类型,在许多编程语言中(甚至面向对象语言C++),数组也是基本类型.但在Java中把数组当做对象来看.因此在创建数组时,必须使用new ...

  8. JS数据结构与算法--双向链表

    双向链表中链接是双向的:一个链向下一个元素,另一个链向上一个元素,如下图所示: 双向链表结构代码如下: class Node { constructor(element) { this.element ...

  9. JS数据结构及算法(一) 堆栈

    最近在看<学习JavaScript数据结构与算法>这本书,感觉自己又涨知识了 哈哈... 现在将自己看的做个总结,也是巩固理解. 栈:先进后出,新添加和待删除的元素都保存在栈顶.可以用数组 ...

随机推荐

  1. 【读书笔记】15《The Bridge of Madison County》

    廊桥遗梦(梅丽尔·斯特里普主演) 罗伯特·詹姆斯·沃勒 99个笔记 The Beginning   美[|diˈklainz]v 辞谢,谢绝(邀请等)( decline的第三人称单数 );(道路.物体 ...

  2. 一文搞懂mysql索引底层逻辑,干货满满!

    一.什么是索引 在mysql中,索引是一种特殊的数据库结构,由数据表中的一列或多列组合而成,可以用来快速查询数据表中有某一特定值的记录.通过索引,查询数据时不用读完记录的所有信息,而只是查询索引列即可 ...

  3. 不建议升级windows11的理由

    此文写于2022年9月13日,用于警告那些蠢蠢欲动想升级win11的伙伴. win11发布后,最亮眼的功能当然是自带"安卓模拟器",但作为一名程序开发者的我其实是不愿意马上尝鲜的, ...

  4. Redis基本数据结构ZipList

    为什么要有ziplist 有两点原因: 普通的双向链表,会有两个指针,在存储数据很小的情况下,我们存储的实际数据的大小可能还没有指针占用的内存大,是不是有点得不偿失?而且Redis是基于内存的,而且是 ...

  5. 第二章:视图层 - 6:QueryDict对象

    类的原型:class QueryDict[source] 在HttpRequest对象中,GET和POST属性都是一个django.http.QueryDict的实例.也就是说你可以按本文下面提供的方 ...

  6. 在k8s中将nginx.conf文件内容创建为ConfigMap挂载到pod容器中

    将nginx.conf文件内容创建为ConfigMap user nginx; worker_processes auto; error_log /var/log/nginx/error.log er ...

  7. Prometheus高可用部署

    Prometheus的本地存储给Prometheus带来了简单高效的使用体验,可以让Promthues在单节点的情况下满足大部分用户的监控需求.但是本地存储也同时限制了Prometheus的可扩展性, ...

  8. 初试Jenkins2.0 Pipeline持续集成

    转载自:https://cloud.tencent.com/developer/article/1010628 1.Jenkins 2.0介绍 先介绍下什么是Jenkins 2.0,Jenkins 2 ...

  9. 记录一次Bitbucket鉴权的坑

    目录 发生了什么 什么原因 如何解决 总结 发生了什么 今天首次在Fedora上使用git,因为没有小王八(TortoiseGit)帮助,其过程异常焦灼-- 反正经过一系列折腾,我在本地新建了一个项目 ...

  10. 220722 T1 分树 (模拟)

    dfs一遍求出以每个节点为根的子树大小,然后枚举n的约数,对于每个约数i,统计sz[ ]是i的倍数的有多少个(开桶统计),如果有n/i个则答案+1. 这道题也就是个结论题,画图分析一下.复杂度O(n* ...