前言:
几乎所有的编程语言都原生支持数组类型。因为数组是最简单的内存数据结构。
创建一个数组:
let arr = new Array()
或
let arr = new Array(5) // 指定长度
或
let arr = new Array(1,2,3,4,5) // 将数组元素作为参数传给构造函数
或
let arr = [1,2,3,4,5] // 中括号创建数组
访问元素和迭代数组:
for (let i = 0; i < arr.length; i ++) {console.log(arr[i])}
斐波那契数列
已知斐波那契数列的前三项分别是0,1,1,从第三项开始,每一项都等于前两项之和。求斐波那契数列前20个数。
const arr = new Array(20);
arr[0] = 0;
arr[1] = 1;
arr[2] = 1;
for (let i = 3;i<arr.length;i++) {
arr[i] = arr[i-1] + arr[i-2]
}
斐波那契数列的经典算法面试题可以看这篇推文->斐波那契数列的算法
添加数组元素
1、在末尾插入元素
在js中,数组是一个可以修改的对象。如果添加元素,它会动态的增长。在其他语言如C和java中,想添加元素需要创建一个全新的数组。不能直接往里面添加元素
arr[arr.length] = 10
或
arr.push(10) //一个
arr.push(10,11) //两个
2、在开头插入元素
arr.unshift(-1)//一个
arr.unshift(-1,-2)//两个
不用unshift怎么实现在开头插入一个元素
var arr = [3,4,5,6]
for (var i = arr.length;i>0;i--){
arr[i] = arr[i-1]
}
arr[0] = 2
![](https://i-blog.csdnimg.cn/blog_migrate/3d3f063ea343b83194131b2a9a2c8014.png)
3、在数组末尾删除元素
arr.pop();
push和pop方法都是用数组模拟栈。
4、在数组开头删除元素
arr.shift();
不用shift怎么实现在开头删除一个元素
Array.prototype.move= function() {
for(var i =0;i<this.length;i++){
this[i] = this[i+1]
}
return this.removeUndefined()
}
Array.prototype.removeUndefined = function(){
const newArr = [];
for (var i = 0; i< this.length;i++) {
if (this[i] !== undefined) {newArr.push(this[i])}
}
return newArr
}
// -----------------------------------------------
var arr = [3,5,6,7,8]
arr.move() // [5,6,7,8]
5、任意位置添加或删除元素
arr.splice(_index,_number,[element]);
// _index:删除或插入的下标
// _number个数
// [element]替换的元素
![](https://i-blog.csdnimg.cn/blog_migrate/61e2c06901b30ac5442d1e42c6081b3f.png)
二维数组(矩阵)
js只支持一维数组,我们可以用数组套数组,实现二维数组(矩阵)或多维数组。
![](https://i-blog.csdnimg.cn/blog_migrate/b32f78d53c0643a531e43d7807ce9308.png)
const dataModel = [
[65,23,45],
[21,11,77]
]
迭代二维数组的元素
for(let i = 0;i < dataModel.length;i++) {
for (let j = 0;j < dataModel[i].length;j++) {
console.log(dataModel[i][j])
}
}
多维数组
// 三维数组
const arr = new Array();
arr[0] = [];
arr[0][0] = [1];
arr[0][0][0] =1;
创建一个3*3正方形魔方立体矩阵
![](https://i-blog.csdnimg.cn/blog_migrate/fc8ff9b95c368d9604569c88a77bb247.jpeg)
const arr = [];
const r = 3; // 正方体边长
for (var x =0;x<r;x++) {
arr[x] = []; //必须层层初始化数组
for (var y=0;y<r;y++) {
arr[x][y] = [];
for (var z=0;z<r;z++) {
arr[x][y][z] = x+y+z;
}
}
}
魔方中最小的值为0+0+0等于0,最大的值为2+2+2=6
![](https://i-blog.csdnimg.cn/blog_migrate/b3000fe8a821f7a445d3f73a7974e15c.png)
循环遍历三维数组
for (var x = 0; x< arr.length; x++) {
for (var y =0;y<arr[x].length;y++) {
for (var z = 0;z<arr[x][y].length;z++) {
console.log(arr[x][y][z])
}
}
}
四维数组使用极少,在此不表。
常用数组方法
方法名 | 对应版本 | 功能 | 原数组改变 |
concat① | - | 合并数组,并返回合并之后的数据 | x |
forEach | ES5 | 遍历数组所有的项。接收回调参数为传参。回调函数接受三个参数,分别为value,index,self;无返回值 | x |
every | ES5 | 同forEach,回调函数对每个数组元素返回布尔值,全部为true,由every返回true | x |
some | ES5 | 同forEach,回调函数对每个数组元素返回布尔值,若有一个为true则返回true | x |
map | ES5 | 同forEach,回调函数对每个数组元素 | x |
filter | ES5 | 同forEach,回调函数返回布尔值,返回结果true的元素组成的新数组 | x |
reduce② | ES5 | 同forEach,迭代数组的所有项,并构建一个最终值,由reduce返回 | x |
join③ | - | 使用分隔符,将数组转为字符串并返回 | x |
indexOf | ES5 | 查询并返回数据的索引 | x |
lastIndexOf | ES5 | 反向查询并返回数据的索引 | x |
reverse | - | 反转数组,返回结果 | √ |
sort | - | 按照字母顺序对数组排序,支持传入指定的排序方法函数作为参数 | √ |
valueOf④ | - | 返回对象 | x |
toString⑤ | - | 将数组作为字符串返回 | x |
①
const arr = [1,2];
arr.concat([3,4,5]); // [1, 2, 3, 4, 5]
arr; // [1,2]
②
// 作用1 求和
const arr = [1,2,3,4,5]
const sum = arr.reduce((result,item) => {
console.log(result) // 计算结果
console.log(item) // 当前元素
return result+item
}, 0)
// 作用2 求数组项最大值
const arr = [1,2,3,4,5]
const sum = arr.reduce((result,item) => {
return Math.max(result,item)
})
// 作用3 扁平一个二维数组
var arr = [[1, 2, 8], [3, 4, 9], [5, 6, 10]];
var res = arr.reduce((prev, cur) => prev.concat(cur), []);
console.log(res) // [1,2,8,3,4,9,5,6,10]
③
var a = [1,2,3]
a.join() // '1,2,3'
a.join('-') // '1-2-3'
④
var a = [1,2,3]
a.valueOf() // [1,2,3]
⑤
var a = [1,2,3]
a.toString() // '1,2,3'
ES6和2015+的数组新功能
copyWithin
copyWithin | ES6 | 浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。 | 改变原数组 |
copyWithin(target, [start], [end])
target:数组索引,复制序列到该位置(原来的元素会被替换掉)。如果是负数,target 将从末尾开始计算。如果 target 大于等于 arr.length,将不会发生拷贝。
start:开始复制元素的起始位置。如果是负数,start 将从末尾开始计算。如果 start 被忽略,copyWithin 将会从 0 开始复制。
end:开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数, end 将从末尾开始计算。如果 end 被忽略,copyWithin 方法将会一直复制至数组结尾(默认为 arr.length)。
// 一个位数复制
const arr = [1,2,3,4,5]
arr.copyWithin(0,1,2) // [2, 2, 3, 4, 5]
arr // [2, 2, 3, 4, 5]
const arr = [1,2,3,4,5]
arr.copyWithin(0,0,1) // [1, 2, 3, 4, 5]
const arr = [1,2,3,4,5]
arr.copyWithin(-1,1,2) // [1, 2, 3, 4, 2]
// 两个位数复制
const arr = [1,2,3,4,5]
arr.copyWithin(0,1,3) // [2, 3, 3, 4, 5]
![](https://i-blog.csdnimg.cn/blog_migrate/81c90ebdadb98f41cda3dc4be758b456.png)
// 全位数复制
const arr = [1,2,3,4,5]
arr.copyWithin() // [1, 2, 3, 4, 5]
includes
includes | ES6 | 如果数组中存在指定元素,则返回true,否则false | 不改变原数组 |
const a = ['aaa','bbb', 'ccc']
a.includes('aaa') // true
a.includes('ddd') // false
find
find | ES6 | 根据回调函数找到数组里符合条件的元素并返回 | 不改变原数组 |
findIndex
findIndex | ES6 | 根据回调函数找到数组里符合条件的元素并返回下标 | 不改变原数组 |
fill
fill | ES6 | 使用固定值填充数组 | 改变原数组 |
var b = []
b.fill(0) // []
var b = [1,1,1]
b.fill(0) // [0, 0, 0]
var b = new Array(5)
b.fill(0) // [0, 0, 0, 0, 0]
from
from | ES6 | 将一个类数组对象或者可遍历对象转换成一个真正的数组。 | 改变原数组 |
什么是类数组对象?
所谓类数组对象,最基本的要求就是具有length属性的对象,并且可以通过索引来访问或设置里面的元素,但是不能使用数组的方法。
let arrayLike = {
0: 'tom',
1: '65',
2: '男',
3: ['jane','john','Mary'],
'length': 4
}
Array.from的第一个作用就是将类数组对象转换为真正数组:
Array.from(arrayLike) // ['tom','65','男',['jane','john','Mary']]
如果将上面代码中length属性去掉呢?那会得到一个长度为0的空数组
let arrayLike = {
0: 'tom',
1: '65',
2: '男',
3: ['jane','john','Mary']
}
Array.from(arrayLike) // []
这里将代码再改一下,就是具有length属性,但是对象的属性名不再是数字类型的,而是其他字符串型的。
let arrayLike = {
'name':'a',
'age': 10,
'length': 2
}
Array.from(arrayLike) //[undefined, undefined]
再修改一下,把length和属性数目脱钩呢?length属性决定了转换为数组的长度
let arrayLike = {
0: 'tom',
1: '65',
'length': 1
}
Array.from(arrayLike) // ['tom']
Array.from的第二个作用就是可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
var a = [1,2,3]
Array.from(a) // [1, 2, 3]
Array.from(a,t => t*2) // [2, 4, 6]
Array.from的第三个作用就是将字符串转换为数组。
const b = 'vme50'
Array.from(b) //['v', 'm', 'e', '5', '0']
of
of | ES6 | 根据传入的参数创建一个新数组 | - |
const arr1 = Array.of(1); // [1]
const arr2 = Array.of(1,2,3); // [1,2,3]
const arr3 = Array.of(...[4,5,6]); // [4,5,6]
// 某种情况下,of跟from的效果一样
const copyArr = [1,2,3]
Array.of(copyArr);
Array.from(copyArr);
for...of
const arr = ['a','b','c']
for (const t of arr) {
console.log(t);
}
// 'a'
// 'b'
// 'c'
迭代器 entries keys values 方法
详见我的另外一篇博文 [戳我]
排序
反向排序
var a = [1,2,3,4]
a.reverse() //[4, 3, 2, 1]
会改变原数组
升序排序
var a = [2,3,1,4]
a.sort() // [1, 2, 3, 4]
会改变原数组
以为这么简单就让你掌握了升序排序?试试这个
var a = [8,10,11,9]
a.sort()
会觉得是8,9,10,11吗?看看结果:
![](https://i-blog.csdnimg.cn/blog_migrate/4c96d0c8c781ddfca9740939b4a99518.png)
为什么呢?因为sort方法在对数组元素进行排序的时候,元素默认成字符串进行相互比较。
我们可以传入比较函数,实现升序或降序排序.
a.sort((x,y) => {...})
升序排序:
a.sort((x,y) => x-y)
降序排序:
a.sort((x,y) => y-x)
拆解和原理请看我的另外一篇博文:戳我
字符串排序
let names = ['Ana', 'ana', 'john', 'John']
names.sort();
// or
names.sort((x,y) => x>y ? 1 : -1);
// ['Ana','John','ana','john']
排序原理请见【附录:字符串排序-ASCii码大小】
如果我们想忽略大小写进行排序,则:
names.sort((x,y) => x.toLowerCase() > y.toLowerCase() ? 1 : -1); // 升序
// (4) ['ana', 'Ana', 'John', 'john']
如果希望小写字母排在前面。
则通过这样比较:
names.sort((x,y) => x.localeCompare(y)); // 升序
// ['ana', 'Ana', 'john', 'John']
这个方法同样对带有重音符号的字符排序也有效。
关于localeCompare详见附录:localeCompare与中文排序
搜索
indexOf
返回与参数匹配的第一个元素的索引
lastIndexOf
返回与参数匹配的最后一个元素的索引
ES6 - find
可接受值类型参数或回调函数(当true返回),返回第一个满足的元素。若无则为undefined
![](https://i-blog.csdnimg.cn/blog_migrate/326b18d813cec2544a816fd5bdcd9630.png)
ES6 - findIndex
同上,返回第一个满足的元素的下标。若无则为-1
可接受索引值进行范围搜索。
ES7 - includes
接受值类型传参。若存在返回true,不存在返回false
接受索引传参。若有传参则从该索引开始搜索(包括传入索引)
var arr = ['a','b','c']
arr.includes('a') // true
arr.includes('a',1) // false
var arr = ['a','a','b','c']
arr.includes('a',1) //true
数组转字符串
var arr = ['a',2,'c']
arr.toString() //'a,2,c'
arr.join('-') //'a-2-c'
arr.join() //'a,2,c'
类型数组
JS与JAVA C这些语言不同,它是弱语言。所以JS的数组可以储存任何类型的值。
为了规范开发,我们希望数组只能存储同种数据类型的值。这个时候我们就需要创建类型数组:
const myArr = new TypeArray();
TypeArray可以替换为:
![](https://i-blog.csdnimg.cn/blog_migrate/c44c6a61d5535c54c16b0dfcf0e7f56c.png)
类型数组在使用webgl API、进行位操作、处理文件、图象大有用处。
TS中的数组
TS中的数组在编译时会进行类型检测,来确保所有数组元素都属于相同数据类型。
const arr = [1,2,3,4]
根据类型判断,ts能够理解arr数组的声明和const arr: number[]是一样的。因此当我们在声明数组对象时赋予了初始值,可以不需要显式声明数组变量的类型。
function testFn(a:Person) {}
interface Person {
name: string,
age: number
}
上面ts规范了一个Person接口,使得函数testFn只能接受的参数类型为{name:xx,age:xxx}的对象。
const friends = [{name:'小明',age:3},{name:'小李',age:4}]
由于friends数组没有显式的类型声明。我们可以这么写:
const friends: Person[]
附录:字符串排序-ASCii码大小
ASCII码编码顺序从小到大为:空格、数字、大写字母、小写字母,也就是空格符、0-9、A-Z、a-z。
记住几个常见字母的ASCII码大小: “A”为65;“a”为97;“0”为 48。
1、数字比字母要小。如 “7”<“F”;
2、数字0比数字9要小,并按0到9顺序递增。如 “3”<“8” ;
3、字母A比字母Z要小,并按A到Z顺序递增。如“A”<“Z” ;
4、同个字母的大写字母比小写字母要小32。如“A”<“a” 。
由于js中 Array.prototype.sort方法会将参数转换为字符串进行排序,由此:
var b = [1,2,3]
b.sort() // [1, 2, 3]
因为0-9的ASCCII码升序排序,所以sort默认升序排序
再看:
var a = ['','a','A','1','a1','aa','aA','11','1a','1A','Aa','AA','A1']
a.sort() // ['', '1', '11', '1A', '1a', 'A', 'A1', 'AA', 'Aa', 'a', 'a1', 'aA', 'aa']
排序顺序依然为顺序,字符串的ASCii码排序规则为:
![](https://i-blog.csdnimg.cn/blog_migrate/b52452fecf646742fd652a8dbbc8e2e9.png)
①空排序,长度为0的排在前面
②对X位进行排序,只要X位Ascii码小的都排在前面,无论长度。
③对X位Ascii码相同的字符串根据Y位进行排序,Y位空的排在最前面,Y位小开始升序排序(依然无视长度)
举个例子:
var arrr = ['a1a','a2','a1A1']
undefined
arrr.sort()// ['a1A1', 'a1a', 'a2'] 小写字母ascii值最大
长度更长的字符串排序顺序也是严格遵守按位排序的规则
附录:localeCompare与中文排序
localeCompare的定义用本地特定的顺序来比较两个字符串。也就是说字符串不再严格遵从Ascii的方法。
![](https://i-blog.csdnimg.cn/blog_migrate/d6e9569bfa4a80129621ca15c192f3fd.png)
基于Ascii的升序排序如上图,而我们
const x = 'a';
const y = 'A1';
x.localeCompare(y) // -1
返回值1:说明当前字符串x大于对比字符串y
返回值-1:说明当前字符串x小于对比字符串y
返回值等于0:说明当前字符串x等于对比字符串y
也就是说a小于A1,升序为a -> A1,与基于Ascii的排序恰恰相反(在中文习惯里,小写要排在大写前面,而Ascii里小写的ascii值最大)
const x = '1';
const y = 'a';
x.localeCompare(y) // -1
由此而知基于中文的localeCompare的升序为:数字<小写字母<大学字母
中文排序
const array = ['刘一','陈二','张三','李四' ,'王五' ,'赵六' ,'孙七','周八','吴九','郑十'];
中文排序我们直观的方法是对文字的拼音进行诸位排序,也就是期待结果为:
['陈二', '李四', '刘一', '孙七', '王五', '吴九', '张三', '赵六', '郑十', '周八']
array.sort()
// ['刘一', '吴九', '周八', '孙七', '张三', '李四', '王五', '赵六', '郑十', '陈二']
可以看见sort排序完全不符合中文习惯
这时我们可以:
array.sort((a, b) =>
a.localeCompare(b)
)
// 如果为了兼容某些浏览器或系统环境
array.sort((a, b) =>
a.localeCompare(b, 'zh-Hans-CN')
)