07 【数组及常用方法】
1.什么是数组
Section titled “1.什么是数组”数组(Array),顾名思义:用来存储一组相关值的类型。
数组也是一个对象,是一个用来存储数据的对象和Object类似,但是它的存储效率比普通对象要高
数组中保存的内容我们称为元素
数组使用索引(index)来操作元素
索引指由0开始的整数
数组可以方便地对一组值进行求和、计算平均值、逐项遍历等操作。
数组名习惯以 Arr 结尾。
2.数组的定义
Section titled “2.数组的定义”2.1 方括号定义法
Section titled “2.1 方括号定义法”2.2 new 定义法
Section titled “2.2 new 定义法”两种定义方法根据实际需求选择即可,两者的底层都是同样的实现逻辑。
推荐:方括号定义法!
- 如果是定义时就要指定数组的值,那么建议使用:
var arr = ['A', 'B', 'C', 'D'];- 如果是定义时还不指定数组的值,那么建议使用:
var arr = [];
3.访问数组项
Section titled “3.访问数组项”注意:JS 中数组的元素可以是不同的数据类型!
数组每一项都有下标,下标从 0 开始!
可以使用 数组名[下标] 的形式,访问数组的任一项。
下标越界
JS 规定,访问数组中不存在的项会返回 undefined,不会报错!
在es2022中,可以通过数组.at(n)来访问,这个可以传递负数作为索引,[1,2].at(-1)结果是-1。
4.数组的长度
Section titled “4.数组的长度”数组的 length 属性表示它的长度。
数组.length
length获取到的是数组的最大索引+1
对于连续的数组,length获取到的就是数组中元素的个数
数组是引用类型,有自己的属性和方法。
数组最后一项的下标是数组的长度减 1。
数组.length = 新长度
如果修改后的length大于原长度,则多出的部分会空出来
如果修改后的length小于原长度,则原数组中多出的元素会被删除
5.更改数组项
Section titled “5.更改数组项”- 访问数组项
- 更改数组值
如果更改的数组项超过了 length-1,则会创造该项。
JS 数组是可以动态扩容的!这就 NB 了!
6.数组的遍历
Section titled “6.数组的遍历”遍历数组就是将数组中元素都获取到
一般情况我们都是使用for循环来遍历数组
forEach()
forEach()方法是对数组的所有成员依次执行参数函数。但是,forEach()方法不返回值,只用来操作数据。
forEach()的参数是一个函数,该函数同样接受三个参数:当前值、当前位置、整个数组。
forEach()方法也可以接受第二个参数,绑定参数函数的this变量。
上面代码中,空数组out是forEach()方法的第二个参数,结果,回调函数内部的this关键字就指向out。
注意,forEach()方法无法中断执行,总是会将所有成员遍历完。如果希望符合某种条件时,就中断遍历,要使用for循环。
上面代码中,执行到数组的第二个成员时,就会中断执行。forEach()方法做不到这一点。
forEach()方法也会跳过数组的空位。
上面代码中,forEach()方法不会跳过undefined和null,但会跳过空位。
总结
forEach()方法需要一个回调函数作为参数,
数组中有几个元素,回调函数就会被调用几次,
每次调用时,都会将遍历到的信息以实参的形式传递进来,
我们可以定义形参来获取这些信息。
value:正在遍历的元素
index:正在遍历元素的索引
obj:被遍历对象
8.数组类型的检测
Section titled “8.数组类型的检测”数组用 typeof 检测结果是 object。
Array.isArray() 方法可以用来检测数组,返回一个布尔值。
isArray() 不兼容 IE678
9.二维数组
Section titled “9.二维数组”二维数组:以数组作为数组元素的数组,即 “数组的数组”。
二维数组可以看做是 “矩阵”。
matrix:矩阵
10.基本类型值和引用类型值
Section titled “10.基本类型值和引用类型值”| 当 var a = b 变量传值时 | 当用 == 或 === 比较时 | |
|---|---|---|
| 基本类型值 | 内存中产生新的副本 | 比较值是否相等(由于是赋值所以类型肯定相同,=== 无需考虑类型是否相等) |
| 引用类型值 | 内存中不产生新的副本,而是让新变量指向同一个对象 | 比较内存地址是否相等,即:比较是否是同一个对象(由于是赋值所以类型肯定相同,=== 无需考虑类型是否相等) |
- 基本类型:
number、boolean、string、undefined、null - 引用类型:
array、object、function、regexp、……
【内存】
【相等 == 判断时的区别】
- 基本类型进行相等
==判断时,会比较 “值” 是否相等 - 引用类型进行相等
==判断时,会比较 “址” 是否相等,也就是说它会比较是否为内存中的同一个东西
11.数组的浅拷贝
Section titled “11.数组的浅拷贝”深拷贝和浅拷贝需要手写代码实现,而不是简单的调用函数。
使用 arr1 = arr2 的语法不会拷贝数组。
浅拷贝:只拷贝数组的第一层,如果是多维数组,或者数组中的项是其他引用类型值,则不拷贝其他层。
深拷贝:拷贝数组的所有层,要用递归技术。
【浅拷贝】
核心思想:“藕断丝连”
数组的常用方法
Section titled “数组的常用方法”12.数组的头尾操作
Section titled “12.数组的头尾操作”| 方法 | 功能 |
|---|---|
push() | 用来向数组的末尾添加一个或多个元素 ,并返回数组新的长度 |
pop() | 用来删除数组的最后一个元素,并返回被删除的元素 |
unshift() | 向数组的开头添加一个或多个元素,并返回数组的新的长度 |
shift() | 删除数组的开头的一个元素,并返回被删除的元素 |
12.1 push() 方法
Section titled “12.1 push() 方法”push() 方法用来向数组的末尾添加一个或多个元素 ,并返回数组新的长度
语法:数组.push(元素1,元素2,元素N)
调用 push() 方法后,数组会立即改变,不需要赋值。
12.2 pop() 方法
Section titled “12.2 pop() 方法”与 push() 方法相反,pop() 方法用来删除数组中的最后一项。
() 里没有参数,默认弹出最后一项。
pop() 默认返回最后一项的值。
12.3 unshift() 方法
Section titled “12.3 unshift() 方法”unshift() 方法用来在数组头部插入新项,参数就是要插入的项。
如果要插入多项,可以用逗号隔开。
调用 unshift() 方法后,数组会立即改变,不需要赋值。
12.4 shift() 方法
Section titled “12.4 shift() 方法”与 unshift() 方法相反,shift() 方法用来删除数组中的开头一项。
() 里没有参数,默认弹出开头一项。
shift() 默认返回开头一项的值。
13.splice() 方法
Section titled “13.splice() 方法”splice() 方法 可以用来删除数组中指定元素,并使用新的元素替换,该方法会将删除的元素封装到新数组中返回。
参数:
1.删除开始位置的索引
2.删除的个数
3.三个以后,都是替换的元素,这些元素将会插入到开始位置索引的前边
由于 splice() 可以实现很多功能,所以也称为 JS 的 “多功能方法”。
- 替换项
- 插入项
- 删除项
splice() 方法会以数组形式返回被替换/删除的项。
14.slice() 方法
Section titled “14.slice() 方法”slice(sart,[end]) 方法会得到新的数组对象,返回数组中被选中的元素,类似于字符串中的 slice() 方法。
slice() 方法选择从给定的 start 参数开始的元素,并在给定的 end 参数处结束,但不包括。
注释: ** slice() 方法不会改变原始数组。**
slice(a, b) 截取的子数组从下标为 a 的项开始,到下标为 b(但不包括下标为 b 的项)结束。
slice(a, b) 方法不会更改原有的数组。
slice() 如果不提供第二个参数,则表示从指定项开始,提取后续所有项作为子数组。
slice() 方法的参数允许为负数,表示数组的倒数第几项(记住不包括最后一项)。
-1 倒数第一个 -2 倒数第二个
15.join() 和 split() 方法
Section titled “15.join() 和 split() 方法”数组的 join([splitor]) 方法可以使 数组 转为 字符串。
字符串的 split() 方法可以使 字符串 转为 数组。
join()的参数表示以什么字符作为连接符,这个字符串将会作为连接符来连接数组中的元素 ,如果不指定连接符则默认使用,,如同调用toString()方法。split()的参数表示以什么字符拆分字符串,一般不能留空。
16.字符串和数组更多相关性
Section titled “16.字符串和数组更多相关性”字符串也可以使用 [下标] 的形式访问某个字符,等价于 charAt() 方法。
在对字符串中的字符进行遍历时不用转为数组,直接利用 [下标] 即可。
字符串的一些算法问题,会转为数组解决!
17.concat() 方法
Section titled “17.concat() 方法”concat() 方法可以连接两个或多个数组,它不会影响原数组,而是新数组作为返回值返回
18.reverse() 方法
Section titled “18.reverse() 方法”reverse() 方法可以用来反转一个数组,它会对原数组产生影响。
【一个小案例】
字符串 'ABCDEFG' 逆序。
19.indexOf() 和 includes() 方法
Section titled “19.indexOf() 和 includes() 方法”indexOf() 方法的功能是搜索数组中的元素,并返回它所在的位置,如果元素不存在,则返回 -1。
includes() 方法的功能是判断一个数组是否包含一个指定的值,如果是返回 true,否则false。
注意:indexOf() 及 includes() 方法的判断标准为 === 全相等!
20.sort() 方法
Section titled “20.sort() 方法”数组排序可以使用 sort() 方法,这个方法的参数又是一个函数,可以对一个数组中的内容进行排序,默认是按照Unicode编码进行排序 ,调用以后,会直接修改原数组。
如果不加参数,那么
sort()默认从小到大排序
可以自己指定排序的规则,需要一个回调函数作为参数,这个函数中 a、b 分别表示数组中靠前和靠后的项。
- 浏览器会根据回调函数的返回值来决定元素的顺序,
如果返回一个大于0的值,则元素会交换位置
如果返回一个小于0的值,则元素位置不变
如果返回一个0,则认为两个元素相等,也不交换位置
对以上算法的优化:
总结
除了内置排序方法外,还有一些排序算法:冒泡排序 和 快速排序 将在后面介绍。
21.toString()方法
Section titled “21.toString()方法”toString方法也是对象的通用方法,数组的toString方法返回数组的字符串形式。
22.数组去重和随机样本
Section titled “22.数组去重和随机样本”【数组去重】
题目:去掉数组中的重复项。
思路:准备一个空结果数组,遍历原数组,如果遍历到的项不在结果数组中,则推入结果数组。
【随机样本】
题目:请随机从原数组中取 3 项。
思路:准备一个空结果数组,遍历原数组,随机选择一项,推入结果数组,并且将这项在原数组中删除。
23.冒泡排序
Section titled “23.冒泡排序”冒泡排序是一个著名的排序算法,也是最基础的交换排序。
冒泡排序的核心思想:一趟一趟地进行多次项的两两比较,每次都会将最大的元素排好位置,如同水中的气泡上浮一样。
时间复杂度:O(n²)

24.快速排序(冒泡排序改进版)
Section titled “24.快速排序(冒泡排序改进版)”快速排序(Quicksort)是使用得最广泛,速度也较快的排序算法。它是图灵奖得主 C. A. R. Hoare(1934—)于 1960 时提出来的。是二十世纪10大算法之一,非常重要!时间复杂度: O(nlogn),稳定性:不稳定。

快速排序与冒泡排序同属交换排序,不过快速排序采用了 “分治法” 的思想大大提高了排序的时间性能。
“快速排序”的思想很简单,整个排序过程只需要三步:
-
在数据集之中,选择一个元素作为 “基准”(pivot)
-
所有小于 “基准” 的元素,都移到 “基准” 的左边;所有大于 “基准” 的元素,都移到 “基准” 的右边
-
对 基准 左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止
举例来说,现在有一个数据集 {85, 24, 63, 45, 17, 31, 96, 50},怎么对其排序呢?
第一步,选择中间的元素 45 作为 “基准”(基准值可以任意选择,但是选择中间的值比较容易理解)。
第二步,按照顺序,将每个元素与 “基准” 进行比较,形成两个子集,一个 “小于45”,另一个 “大于等于45”。
第三步,对两个子集不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
下面用 Javascript 语言实现上面的算法。
首先,定义一个 quickSort 函数,它的参数是一个数组。
然后,检查数组的元素个数,如果小于等于 1,就返回。
接着,选择 “基准”(pivot),并将其与原数组分离,再定义两个空数组,用来存放一左一右的两个子集。
然后,开始遍历数组,小于 “基准” 的元素放入左边的子集,大于基准的元素放入右边的子集。
最后,使用递归不断重复这个过程,就可以得到排序后的数组。