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