JavaScript数组
数组(Array)是属于内置对象,数组和普通对象的功能类似,都可以用来存储一些值。不同的是:
- 普通对象是使用字符串作为属性名,而数组是使用数字作为索引来操作元素。索引:从 0 开始的整数就是索引。
数组的存储性能比普通对象要好。在实际开发中我们经常使用数组存储一些数据(尤其是列表数据),使用频率非常高。
数组创建
在js中有一下几种方式创建数组
使用字面量创建数组
let arr1 = []; // 创建一个空的数组
let arr2 = [1, 2, 3]; // 创建带初始值的数组
使用构造函数创建数组
let arr = new Array(参数);
let arr = Array(参数);
如果参数为空 ,表示创建一个空数组;如果参数是一个数值 ,表示数组的长度;如果有多个参数,表示数组中的元素内容。
let arr1 = [1,2,3]
let arr2 = new Array()
let arr3 = new Array(5)
let arr4 = new Array(1,"hello",{"name":'tom', 'age':12},[3,6])
console.log('arr1 = ' + JSON.stringify(arr1)); // arr1 = [1,2,3]
console.log('arr2 = ' + JSON.stringify(arr2)); // arr2 = []
console.log('arr3 = ' + JSON.stringify(arr3)); // arr3 = [null,null,null,null,null]
console.log('arr4 = ' + JSON.stringify(arr4)); // arr4 = [1,"hello",{"name":"tom","age":12},[3,6]]
以上例子我们可以看出数组中可以存放任意类型的数据,例如字符串、数字、布尔值、对象等。
数组的操作
索引 (下标) :用来访问数组元素的序号,代表的是数组中的元素在数组中的位置(下标从 0 开始算起)。
添加元素
let arr1 = new Array(5)
arr1[1]= 100
arr1[3] = 0
arr1[6] = 2
console.log('arr1 = ' + JSON.stringify(arr1)); // arr1 = [null,100,null,0,null,null,2]
当下标查过定义数组的长度时候, 会把数组的长度扩充
获取元素
如果读取不存在的索引(比如元素没那么多),系统不会报错,而是返回 undefined。
代码举例:
const arr = [21, 22, 23];
console.log(arr[0]); // 打印结果:21
console.log(arr[5]); // 打印结果:undefined
数组长度
可以使用length
属性来获取数组的长度(即"元素的个数")。
数组的长度是元素个数,不要跟索引号混淆。
语法:
数组的长度 = 数组名.length;
代码举例:
const arr = [21, 22, 23];
console.log(arr.length); // 打印结果:3
补充:
对于连续的数组,使用 length 可以获取到数组的长度(元素的个数);对于非连续的数组(即"稀疏数组",本文稍后会讲),length 的值会大于元素的个数。因此,尽量不要创建非连续的数组。
修改数组长度
可以通过修改length属性修改数组的长度。
-
如果修改的 length 大于原长度,则多出部分会空出来,置为 null。
-
如果修改的 length 小于原长度,则多出的元素会被删除,数组将从后面删除元素。
-
(特例:伪数组 arguments 的长度可以修改,但是不能修改里面的元素,以后单独讲。)
const arr1 = [11, 12, 13];
const arr2 = [21, 22, 23];// 修改数组 arr1 的 length
arr1.length = 1;
console.log(JSON.stringify(arr1)); // [11]// 修改数组 arr2 的 length
arr2.length = 5;
console.log(JSON.stringify(arr2)); // [21, 22, 23, null, null]
遍历数组
就是把数组中的每个元素从头到尾都访问一次。最简单的做法是通过 for 循环,遍历数组中的每一项。
let arr1 = [1,"dany",12,{"name":"tom","age":12},[33,44]]
for (let index = 0; index < arr1.length; index++) {
console.log(arr1[index]);
}
和其他编程语言相比,JS语言中的数组比较灵活,有许多与众不同的地方。
1、如果访问数组中不存在的索引时,不会报错,会返回undefined。
2、当数组的存储空间不够时,数组会自动扩容。其它编程语言中数组的大小是固定的,不会自动扩容。
3、数组可以存储不同类型数据,其它编程语言中数组只能存储相同类型数据。
4、数组分配的存储空间不一定是连续的。其它语言数组分配的存储空间是连续的。
JS中的数组采用"哈希映射"的方式分配存储空间,我们可以通过索引找到对应空间。各大浏览器也对数组分配的存储空间进行了优化:如果存储的都是相同类型的数据,则会尽量分配连续的存储空间;如果存储的不是相同的数据类型,则不会分配连续的存储空间。
数组解构
解构赋值是ES6中新增的一种赋值方式。
ES5中,如果想把数组中的元素赋值给其他变量,是这样做的:
const arr = [1, 2, [3,4]];
let a = arr[0]; // 1
let b = arr[1]; // 2
let c = arr[2]; // [3, 4]
//我们也可以按照如下方式
let [a, b, c] = [1, 2, [3, 4]];
console.log(a); // 1
console.log(b); // 2
console.log(c); // [3, 4]
注意点:
(1)等号左边的个数和格式,必须和右边的一模一样,才能完全解构。
(2)当然,左边的个数和右边的个数,可以不一样。
解构时,我们也可以给出默认值
let [a, b = 3, c = 4] = [1, 2];
console.log(a); // 1
console.log(b); // 2。默认值被覆盖。
console.log(c); // 4。继续保持默认值。
我们可以使用ES6中新增的扩展运算符打包剩余的数据。如果使用了扩展运算符, 那么扩展运算符只能写在最后。代码举例:
let [a, ...b] = [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]
数组的方法
数组类型判断
方法 | 描述 | 备注 |
---|---|---|
Array.isArray() | 判断是否为数组 | |
toString() | 将数组转换为字符串 | 不会改变原数组 |
join() | 将数组转换为字符串,返回结果为转换后的字符串 | 不会改变原数组 |
字符串的方法:split() | 将字符串按照指定的分隔符,组装为数组 | 不会改变原字符串 |
Array.from(arrayLike) | 将伪数组 转化为真数组 | |
Array.of(value1, value2, value3) | 创建数组:将一系列值转换成数组 |
注意:
(1)获取数组的长度是用length
属性,不是方法。关于 length
属性,详见上一篇文章。
(2)split()
是字符串的方法,不是数组的方法。
isArray() 判断是否为数组
toString()
join()
arr1 = [1,"tom",{"name":"jack","age":12},[22,55]]
console.log(Array.isArray(arr1)) //true 判断是否为数组
console.log(arr1.toString()) // 1,tom,[object Object],22,55 数组转为字符串 也可以用String(arr1)方法
//将数组转换为字符串,返回结果为转换后的字符串(不会改变原来的数组)。 join()方法可以指定一个字符串作为参数,这个参数是元素之间的连接符;如果不指定连接符,则默认使用英文逗号, 作为连接符,此时和 toString()的效果是一致的。
console.log(arr1.join(",")) // 1,tom,[object Object],22,55
伪数组是包含 length 属性的对象或可迭代的对象。伪数组的原型链中没有 Array.prototype,而真数组的原型链中有 Array.prototype。因此伪数组没有数组的一般方法,比如 pop()、join() 等方法。例如我们经常通过DOM获取页面上具有相同属性的一系列元素, 此时得到的数据就是伪数组,我们可以通过
array = Array.from(arrayLike);
将伪数组 或可遍历对象转换为真数组。
数组元素添加与删除
方法 | 描述 | 备注 |
---|---|---|
push() | 向数组的最后面 插入一个或多个元素,返回结果为新数组的长度 | 会改变原数组 |
pop() | 删除数组中的最后一个 元素,返回结果为被删除的元素 | 会改变原数组 |
unshift() | 在数组最前面 插入一个或多个元素,返回结果为新数组的长度 | 会改变原数组 |
shift() | 删除数组中的第一个 元素,返回结果为被删除的元素 | 会改变原数组 |
splice() | 从数组中删除 指定的一个或多个元素,返回结果为被删除元素组成的新数组 | 会改变原数组 |
concat() | 合并数组:连接两个或多个数组,返回结果为新的数组 | 不会改变原数组 |
fill() | 填充数组:用固定的值填充数组,返回结果为新的数组 | 会改变原数组 |
push()
push():向数组的最后面插入一个或多个元素,返回结果为新数组的长度。会改变原数组,因为原数组变成了新数组
pop()
pop():删除数组中的最后一个元素,返回结果为被删除的元素
unshift()
在数组最前面插入一个或多个元素,返回结果为新数组的长度。会改变原数组,将原数组变成了新数组。插入元素后,其他元素的索引会依次调整。
shift()
shift():删除数组中的第一个元素,返回结果为被删除的元素。
arr1 = [1,"tom",{"name":"jack","age":12},[22,55]]
console.log(arr1.push(100)) // 5
console.log(arr1) //[ 1, 'tom', { name: 'jack', age: 12 }, [ 22, 55 ], 100 ]
// pop():删除数组中的最后一个元素,返回结果为被删除的元素
console.log(arr1.pop()) // 100
console.log(arr1) // [ 1, 'tom', { name: 'jack', age: 12 }, [ 22, 55 ] ]
//unshift():在数组最前面插入一个或多个元素,返回结果为新数组的长度。会改变原数组,将原数组变成了新数组。插入元素后,其他元素的索引会依次调整。
console.log(arr1.unshift(23,[34,56])) // 6
console.log(arr1) // [ 23, [ 34, 56 ], 1, 'tom', { name: 'jack', age: 12 }, [ 22, 55 ] ]
//shift():删除数组中的第一个元素,返回结果为被删除的元素。
console.log(arr1.shift() ) // 23
console.log(arr1) // [ [ 34, 56 ], 1, 'tom', { name: 'jack', age: 12 }, [ 22, 55 ] ]
splice()
splice():从数组中删除指定的一个或多个元素,返回结果为被删除元素组成的新数组(会改变原来的数组)。该方法会改变原数组,会将指定元素从原数组中删除;被删除的元素会封装到一个新的数组中返回。
新数组 = 原数组.splice(起始索引index);
新数组 = 原数组.splice(起始索引index, 需要删除的个数);
新数组 = 原数组.splice(起始索引index, 需要删除的个数, 新的元素1, 新的元素2...);
上方语法中,第三个及之后的参数,表示:删除元素之后,向原数组中添加新的元素,这些元素将会自动插入到起始位置索引的前面。也可以理解成:删除了哪些元素,就在那些元素的所在位置补充新的内容。slice()方法和splice()方法很容易搞混,请一定要注意区分。
arr1 = [1,"tom",{"name":"jack","age":12},[22,55]]
var res = arr1.splice(1)
console.log(arr1) // [1]
console.log(res) //[ 'tom', { name: 'jack', age: 12 }, [ 22, 55 ] ]
arr1 = [1,"tom",{"name":"jack","age":12},[22,55]]
var res = arr1.splice(1,2) // //从第index为1的位置开始删除元素,一共删除三个元素
console.log(arr1) // [ 1, [ 22, 55 ] ]
console.log(res) // [ 'tom', { name: 'jack', age: 12 } ]
arr1 = [1,"tom",{"name":"jack","age":12},[22,55]]
var res = arr1.splice(1,2, "replase","replace","replace")
console.log(arr1) // [ 1, 'replase', 'replace', 'replace', [ 22, 55 ] ]
console.log(res) // [ 'tom', { name: 'jack', age: 12 } ]
concat()
concat()
:连接两个或多个数组,返回结果为新的数组 。不会改变原数组。concat()
方法的作用是数组合并。
新数组 = 数组1.concat(数组2, 数组3 ...);
//我们可以使用...这种扩展运算符,将两个数组进行合并
const arr1 = [1, 2, 3];
const result = ['a', 'b', 'c', ...arr1];
slice()
slice()
:从数组中提取 指定的一个或者多个元素,返回结果为新的数组(不会改变原来的数组)。
备注:该方法不会改变原数组,而是将截取到的元素封装到一个新数组中返回。
新数组 = 原数组.slice(开始位置的索引);
新数组 = 原数组.slice(开始位置的索引, 结束位置的索引); //注意:提取的元素中,包含开始位置,不包含结束位置
fill()
fill()
:用一个固定值填充数组,返回结果为新的数组。会改变原数组。
// 用一个固定值填充数组。数组里的每个元素都会被这个固定值填充
新数组 = 数组.fill(固定值);
// 从 startIndex 开始的数组元素,用固定值填充
新数组 = 数组.fill(固定值, startIndex);
// 从 startIndex 到 endIndex 之间的元素(包左不包右),用固定值填充
新数组 = 数组.fill(固定值, startIndex, endIndex);
数组排序
方法 | 描述 | 备注 |
---|---|---|
reverse() | 反转数组,返回结果为反转后的数组 | 会改变原数组 |
sort() | 对数组的元素,默认按照Unicode 编码,从小到大进行排序 | 会改变原数组 |
reverse()
reverse()
:反转数组,返回结果为反转后的数组(会改变原来的数组)。
var arr1 = [1,3,2,5,6]
arr1.reverse()
console.log(arr1) // [ 6, 5, 2, 3, 1 ]
sort()
sort()
:对数组的元素进行从小到大来排序(会改变原来的数组)。
var arr1 = [1,3,12,5,6]
arr1.sort()
console.log(arr1) // [ 1, 12, 3, 5, 6 ]
上述排序大家肯定有疑惑, 为什么12 排在3的前面,这是因为sort()
方法是按照Unicode 编码进行排序的。要想让数组里面的数据按照我们想的排序 我们需要在sort里面定义参数
如果在 sort()方法中带参,我们就可以自定义排序规则。具体做法如下:
我们可以在 sort()的参数中添加一个回调函数,来指定排序规则。回调函数中需要定义两个形参,JS将会分别使用数组中的元素作为实参去调用回调函数。
JS根据回调函数的返回值来决定元素的排序:(重要)
-
如果返回一个大于 0 的值,则元素会交换位置
-
如果返回一个小于 0 的值,则不交换位置。
-
如果返回一个等于 0 的值,则认为两个元素相等,则不交换位置
var arr1 = [1,3,12,5,6]
arr1.sort(function (a, b) {
return a - b;})
console.log(arr1) // [ 1, 3, 5, 6, 12 ]
//跟简单写法
arr.sort((a, b) => a - b);
下面看一个复杂的例子
var arr1 = [{'age':2,'score':34},{'age':12,'score':24},{'age':5,'score':63},{'age':21,'score':87}]
arr1.sort(function (a, b) {
return a.age - b.age;
})
console.log(arr1)
// [
// { age: 2, score: 34 },
// { age: 5, score: 63 },
// { age: 12, score: 24 },
// { age: 21, score: 87 }
// ]
查找元素
方法 | 描述 | 备注 |
---|---|---|
indexOf(value) | 从前往后索引,检索一个数组中是否含有指定的元素 | |
lastIndexOf(value) | 从后往前索引,检索一个数组中是否含有指定的元素 | |
includes(item) | 数组中是否包含指定的内容 | |
find(function()) | 找出第一个满足「指定条件返回 true」的元素 | |
findIndex(function()) | 找出第一个满足「指定条件返回 true」的元素的 index | |
every() | 确保数组中的每个元素都满足「指定条件返回 true」,则停止遍历,此方法才返回 true | 全真才为真。要求每一项都返回 true,最终的结果才返回 true |
some() | 数组中只要有一个元素满足「指定条件返回 true」,则停止遍历,此方法就返回 true | 一真即真。只要有一项返回 true,最终的结果就返回 true |
indexOf()/lastIndexOf():获取元素的索引
索引值 = 数组.indexOf(想要查找的元素, [查找的起始位置]);
元素的索引 = 数组.lastIndexOf(想要查询的元素);
indexOf()
是从左往右查找元素的位置。同理,lastIndexOf()
是从右往左寻找。
解释 :可以检索一个数组中是否含有指定的元素。如果数组中含有该元素,则会返回其第一次出现的索引,并立即停止查找;如果没有找到指定的内容,则返回 -1。
这个方法的作用:
- 如果找到了指定的元素,就返回元素对应的位置。
- 如果没有找到指定的元素,就会返回-1。
includes()
判断一个数组中是否包含指定的元素。如果是,则会返回 true;否则返回 false。
布尔值 = arr.includes(想要查找的元素, [position]);
参数中的 position
:如果不指定,则默认为0;如果指定,则规定了检索的起始位置。
find()
找出第一个满足「指定条件返回 true」的元素,并立即停止查找;如果没找到,则返回 undefined。
备注:一旦找到符合条件的第一个元素,将不再继续往下遍历。
const itemResult = arr.find((currentItem, currentIndex, currentArray) => {
return true;
});
var arr1 = [23,"tom",{"name":"jack",'age':12},[23,56], '22']
var res = arr1.find((item, index)=>{
if (typeof(item) == 'object') { //找到是对象的元素打印出来
console.log(item)
}
});
// { name: 'jack', age: 12 }
// [ 23, 56 ]
findIndex()
找出第一个满足「指定条件返回 true」的元素的索引,并立即停止遍历;如果没找到,则返回 -1。
const indexResult = arr.findIndex((currentItem, currentIndex, currentArray) => {
return true;
});
var arr1 = [23,"tom",{"name":"jack",'age':12},[23,56], '22']
var res = arr1.findIndex((item, index)=>{
if (typeof(item) == 'object') {
return 1;
}
});
console.log(res) // 2
every()
对数组中每一项运行回调函数,如果都返回 true,every 就返回 true;如果有一项返回 false,则停止遍历,此方法返回 false。
注意:every()方法的返回值是 boolean 值,参数是回调函数。
const boolResult = arr.every((currentItem, currentIndex, currentArray) => {
return true;
});
some()
对数组中每一个元素运行回调函数,只要有一个元素返回 true,则停止遍历,此方法返回 true。
注意:some()方法的返回值是 boolean 值。
var arr1 = [23,"tom",{"name":"jack",'age':12},[23,56], '22']
var res = arr1.every((item, index)=>{
if (item.length >2) {
return 0;
}
return 1;
});
console.log(res) // false
var arr1 = [23,"tom",{"name":"jack",'age':12},[23,56], '22']
var res = arr1.some((item, index)=>{
if (item.length >2) {
return 0;
}
return 1;
});
console.log(res) // true
every() 和 some() 这两个方法,初学者很容易搞混。要怎么区分呢?你可以这样记:
- every():全部真,才为真。当你需要让数组中的每一个元素都满足指定条件时,那就使用 every()。
- some():一个真,则为真,点到为止。数组中只要有一个元素满足指定条件时,就停止遍历。那就使用 some()。
遍历数组
方法 | 描述 | 备注 |
---|---|---|
for 循环 | 最传统的方式遍历数组,这个大家都懂 | |
forEach() | 遍历数组,但需要兼容 IE8 以上 | 不会改变原数组。forEach() 没有返回值。也就是说,它的返回值是 undefined |
for of | 遍历数组(ES6语法) | 不会改变原数组。另外,不要使用 for in 遍历数组 |
map() | 对原数组中的每一项进行加工,将组成新的数组 | 不会改变原数组 |
filter() | 过滤数组:返回结果是 true 的项,将组成新的数组,返回结果为新的数组 | 不会改变原数组 |
reduce | 接收一个函数作为累加器,返回值是回调函数累计处理的结果 | 比较复杂 |
获取并操作数组中的每一个元素,然后得到想要的返回结果。在实战开发中使用得非常频繁。
for循环遍历
var arr1 = [23,"tom",{"name":"jack",'age':12},[23,56], '22']
for (let index = 0; index < arr1.length; index++) {
const element = arr1[index];
console.log(element)
}
forEach遍历
// ES5语法
arr.forEach(function (currentItem, currentIndex, currentArray) {
console.log(currentValue);
});
// ES6语法
arr.forEach((currentItem, currentIndex, currentArray) => {
console.log(currentValue);
});
forEach()方法需要一个函数作为参数。这种函数,是由我们创建但是不由我们调用的,我们称为回调函数。
数组中有几个元素,该回调函数就会执行几次。
回调函数中传递三个参数:
- 参数1:当前正在遍历的元素
- 参数2:当前正在遍历的元素的索引
- 参数3:正在遍历的数组
注意,forEach() 没有返回值。也可以理解成:forEach() 的返回值是 undefined。如果你尝试 newArray = currentArray.forEach()
这种方式来接收,是达不到效果的。
var arr1 = [23,"tom",{"name":"jack",'age':12},[23,56], '22']
arr1.forEach((item,index)=> {
console.log(item)
})
for of 遍历
for(let value of arr) {
console.log(value);
}
map()
// ES5语法
const newArr = arr.map(function (currentItem, currentIndex, currentArray) {
return newItem;
});
// ES6语法
const newArr = arr.map((currentItem, currentIndex, currentArray) => {
return newItem;
});
对数组中每一项运行回调函数,返回该函数的结果,组成的新数组(返回的是加工后的新数组)。不会改变原数组。作用:对数组中的每一项进行加工。
const arr1 = [1, 3, 6, 2, 5, 6];
const arr2 = arr1.map(item => {
return item + 10; //让arr1中的每个元素加10
});
console.log(arr2); // 数组 arr2 的值:[11, 13, 16, 12, 15, 16]
filter()
const newArr = arr.filter((currentItem, currentIndex, currentArray) => {
return true;
});
对数组中的每一项运行回调函数,该函数返回结果是 true 的项,将组成新的数组(返回值就是这个新数组)。不会改变原数组。作用:对数组进行过滤。
let arr1 = [1, 3, 6, 2, 5, 6];
let arr2 = arr1.filter(item => {
if (item > 4) {
return true; // 将arr1中大于4的元素返回,组成新的数组
}
return false;
});
console.log(JSON.stringify(arr1)); // 打印结果:[1,3,6,2,5,6]
console.log(JSON.stringify(arr2)); // 打印结果:[6,5,6]
reduce()
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。返回值是回调函数累计处理的结果。
语法:
arr.reduce(function (previousValue, currentValue, currentIndex, arr) {}, initialValue);
参数解释:
- previousValue:必填,上一次调用回调函数时的返回值
- currentValue:必填,当前正在处理的数组元素
- currentIndex:选填,当前正在处理的数组元素下标
- arr:选填,调用 reduce()方法的数组
- initialValue:选填,可选的初始值(作为第一次调用回调函数时传给 previousValue 的值)
在以往的数组方法中,匿名的回调函数里是传三个参数:item、index、arr。但是在 reduce() 方法中,前面多传了一个参数previousValue
,这个参数的意思是上一次调用回调函数时的返回值。第一次执行回调函数时,previousValue 没有值怎么办?可以用 initialValue 参数传给它。
备注:绝大多数人在一开始接触 reduce() 的时候会很懵逼,但是没关系,有事没事多看几遍,自然就掌握了。如果能熟练使用 reduce() 的用法,将能替代很多其他的数组方法,并逐渐走上进阶之路,领先于他人。
let arr1 = [1, 2, 3, 4, 5, 6];
arr1.reduce((prev, item) => {
console.log(prev);
console.log(item);
console.log('------');
return 88;
}, 0);
打印结果:
0
1
------
88
2
------
88
3
------
88
4
------
88
5
------
88
6
------
上面的代码中,由于return
的是固定值,所以 prev 打印的也是固定值(只有初始值是 0,剩下的遍历中,都是打印 88)。
现在来升级一下,实际开发中,prev 的值往往是动态变化的,这便是 reduce()的精妙之处。我们来看几个例子就明白了。