本文是 重温基础 系列文章的第十篇。
今日感受:平安夜,多棒。
本章节复习的是JS中的数组,以索引进行排序。
前置知识:
数组是一个有序的数据集合,使用数组名称和索引进行访问。
-
let arr = [1,2,3];
-
arr[0] = 1;
在JavaScript中数组没有明确数据类型。
-
let arr = [1, 'hi', undefined, fun()];
1.创建数组
创建数组方法有3种:
-
let arr = new Array(ele1, ele2, ele3, ..., eleN);
-
let arr = Array(ele1, ele2, ele3, ..., eleN);
-
let arr = [ele1, ele2, ele3, ..., eleN];
上面是已知数组元素,另外一种还有创建一个长度不为0,且有没有任何元素的数组:
-
let len = 5;
-
let arr = new Array(len); // 方法1
-
let arr = Array(len); // 方法2
-
let arr = []; // 方法3
-
arr.length = len;
若传入的数组长度不是整数,则报错:
-
let arr = new Array(3.5);
-
let arr = Array(3.5);
-
let arr = [];
-
arr.length = 3.5;
-
//Uncaught RangeError: Invalid array length
其中要注意这两种创建方法是不同的:
-
let arr1 = new Array(4); // [empty × 4]
-
let arr2 = [4]; // [4]
-
for(let k in arr1){
-
console.log(k);
-
} // undefined
-
for(let k in arr2){
-
console.log(k);
-
} // 0
2.使用数组
2.1 简单使用
获取数组指定位置的值:
-
let a = [1,2,5];
-
a[0]; // 1
-
a[2]; // 5
-
a[3]; // undefined
获取数组长度:
-
let a = [1,2,5];
-
a.length; // 3
-
a["length"]; // 3
设置数组指定位置的值:
-
let a = [1,2,5];
-
a[0] = 9;
-
a[2] = 99;
-
a[3] = 999;
2.2 理解数组length
- 数组的索引值是从
0
开始,即上面数组索引 0
的是 1
,索引 1
的值是 2
,依次下去。 - 数组
length
永远返回的是数组最后一个元素的索引加1。 - 可通过
arr.length=0
来清空数组。 - 可通过
arr.length=len
来设置数组长度。
2.3 遍历数组
遍历数组就是以某种方法处理数组的每个元素,简单如下:
- 使用
for
循环:
-
let arr = ["pingan", "leo", "robin"];
-
for (let i = 0; i<arr.length; i++){
-
// 处理元素的操作
-
console.log(`第${i}个元素是:${arr[i]};`)
-
}
-
// 第0个元素是:pingan;
-
// 第1个元素是:leo;
-
// 第2个元素是:robin;
- 使用
for...in
:
-
let arr = ["pingan", "leo", "robin"];
-
for(let i in arr){
-
console.log(`第${i}个元素是:${arr[i]};`)
-
}
-
// 第0个元素是:pingan;
-
// 第1个元素是:leo;
-
// 第2个元素是:robin;
- 使用
forEach
:
arr.forEach(callback)
接收一个回调方法。
callback(val,index,array)
: 接收三个参数:
-
val
: 当前处理的元素; -
index
: 当前处理的元素的索引; -
array
: 正在处理的数组;
可参考MDN Array.prototype.forEach 的详细介绍。
-
let arr = ["pingan", "leo", "robin"];
-
arr.forEach(function(val, i, array){
-
console.log(`第${i}个元素是:${val};`)
-
})
3. 数组方法(访问和修改)
方法名称 | 方法介绍 |
| 连接两个或更多的数组,并返回结果。 |
| 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。 |
| 删除并返回数组的最后一个元素 |
| 向数组的末尾添加一个或更多元素,并返回新的长度。 |
| 颠倒数组中元素的顺序。 |
| 删除并返回数组的第一个元素 |
| 从某个已有的数组返回选定的元素 |
| 对数组的元素进行排序 |
| 删除元素,并向数组添加新元素。 |
| 返回该对象的源代码。 |
| 把数组转换为字符串,并返回结果。 |
| 把数组转换为本地数组,并返回结果。 |
| 向数组的开头添加一个或更多元素,并返回新的长度。 |
| 返回数组对象的原始值 |
| 在数组中搜索指定元素并返回第一个匹配的索引 |
| 在数组中搜索指定元素并返回最后一个匹配的索引 |
可参考W3school JavaScript Array 对象 的详细介绍。
3.1 concat()
连接两个或更多的数组,并返回一个新数组。
- 语法:
arr.concat(a1,a2,...,an)
; - 参数:
arr
:目标数组;
a1,a2,...,an
:需要合并的元素;
-
let a1 = [1,2,3];
-
let a2 = [9,99,999];
-
let a = a1.concat(a2);
-
// [1, 2, 3, 9, 99, 999]
3.2 join()
使用指定分隔符,连接两个或多个数组的元素,返回一个字符串。
- 语法:
arr.join(sep)
; - 参数:
arr
:目标数组;
sep
:连接的分隔符,默认值为“ ,
”;
-
let arr = ["pingan", "leo", "robin"];
-
arr.join(); // "pingan,leo,robin"
-
arr.join(""); // "pinganleorobin"
-
arr.join(","); // "pingan,leo,robin"
3.3 pop()和push()
pop()
: 删除并返回数组最后一个元素,改变原数组。
push(item)
: 向数组末尾添加一个或多个元素,改变原数组,返回新的数组长度。
方便记忆和理解:两个都是从数组末尾操作, pop()
是删除最后一个元素, push()
是向最后一位添加新的元素。
-
let arr = ["pingan", "leo"];
-
let a1 = arr.pop(); // "leo"
-
let a2 = arr.push("robin","hi"); // 3
-
arr; // ["pingan", "robin", "hi"]
3.4 shift()和unshift()
-
shift()
: 删除并返回数组第一个元素,改变原数组。 -
unshift(item)
: 向数组头部添加一个或多个元素,改变原数组,返回新的数组长度。
方便记忆和理解:两个都是从数组头部操作, shift()
是删除第一个元素, unshift()
是向第一位添加新的元素。
-
let arr = ["pingan", "leo"];
-
let a1 = arr.shift(); // "pingan"
-
let a2 = arr.unshift("robin","hi"); // 3
-
arr; // ["robin", "hi", "leo"]
3.5 reverse()
颠倒数组中元素的顺序,改变原数组。
-
let arr = [1, 2, 3, 4];
-
arr.reverse(); // [4, 3, 2, 1]
3.6 slice()
用于提取数组中一个片段,作为新数组返回。
slice(start[,end])
: 接收2个参数:
-
start
: 必需,指定起始索引,若负数则从数组最后开始算起, -1
为倒数第一位, -2
为倒数第二位,以此类推。 -
end
: 可选,指定结束索引,若没传则表示到数组结束。
注意:
end
若有指定的话,是不包含 end
索引上的值。
-
let arr = [1, 2, 3, 5, 6];
-
let a1 = arr.slice(2); // [3, 5, 6]
-
let a2 = arr.slice(2,3); // [3]
3.7 splice()
从数组中删除指定索引开始的项目,然后返回被删除的项目。
- 语法:
arr.splice(index,num,a1,a2,...,an)
; - 参数:
index
: 必需,起始位置的索引,若负数则从数组最后开始算起;
num
:必需,删除的数量,若为0则不删除;
a1,a2,...an
:可选,为数组添加的元素;
-
let arr = [1, 2, 3, 4];
-
let a = arr.splice(1, 2, "hi", "leo");
-
// a => [2, 3]
-
// arr => [1, "hi", "leo", 4]
3.8 sort()
对数组的元素进行排序,改变原数组。
可接受一个回调方法作为比较函数,来决定排序方式。
比较函数应该具有两个参数 a
和 b
,返回值如下:
若 a
小于 b
,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。 若 a
等于 b
,则返回 0。 若 a
大于 b
,则返回一个大于 0 的值。
-
let a1 = [1,3,6,9,10];
-
a1.sort(); // [1, 10, 3, 6, 9]
-
a1.sort(function(a,b){
-
return a > b ? 1 : a < b ? -1 : 0;
-
}) // [1, 3, 6, 9, 10]
3.9 indexOf()和lastIndexOf()
两者都是在数组搜索指定元素,只是 indexOf()
返回的是搜索到的第一个元素的索引,而 lastIndexOf()
返回的是搜索到的最后一个元素的索引。
语法:
indexOf(ele[,start])
和 lastIndexOf(ele[,start])
;
参数:
-
ele
: 需要搜索的元素。 -
start
: 开始搜索的索引。
-
let arr = ["hh1", "hh2", "hh2", "hh2", "hh3", "hh4"];
-
let a1 = arr.indexOf("hh2"); // 1
-
let a2 = arr.lastIndexOf("hh2"); // 3
-
let a3 = arr.indexOf("hh2",2); // 2
4. 数组方法(迭代)
方法名称 | 方法介绍 |
| 为数组中的每个元素执行一次回调函数。 |
| 如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false。 |
| 如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。 |
| 将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回。 |
| 返回一个由回调函数的返回值组成的新数组。 |
| 从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。 |
| 从右到左为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。 |
以下是ES6规范新增的数组方法:
方法名称 | 方法介绍 |
| 返回一个数组迭代器对象,该迭代器会包含所有数组元素的键。 |
| 返回一个数组迭代器对象,该迭代器会包含所有数组元素的值。 |
| 返回一个数组迭代器对象,该迭代器会包含所有数组元素的键值对。 |
| 找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined。 |
| 找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1。 |
可参考MDN Array 的详细介绍。
4.1 forEach()
对数组的每个元素执行一次提供的函数。
语法:
arr.forEach(callback)
。
参数:
callback(val,index,arr)
: 需要执行的函数,接收三个参数:
-
val
: 正在处理的当前元素; -
index
: 可选,正在处理的当前元素的索引; -
arr
: 可选,正在操作的数组;
-
let a = [1,3,5,7];
-
a.forEach(function(val, index, arr){
-
arr[index] = val * 2
-
})
-
a ; // [2, 6, 10, 14]
4.2 every()
测试数组的所有元素是否都通过了指定函数的测试。
语法:
arr.every(callback)
。
参数:
callback(val,index,arr)
: 需要执行的函数,接收三个参数:
-
val
: 正在处理的当前元素; -
index
: 可选,正在处理的当前元素的索引; -
arr
: 可选,正在操作的数组;
返回值:
若都通过返回 true
,否则返回 false
。
-
let a = [1, "", "aa", 13, 6];
-
let res = a.every(function(val, index, arr){
-
return typeof val == "number";
-
})
-
res;// false
-
let b = [1, 2, 3];
-
let r = b.every(function(val, index, arr){
-
return typeof val == "number";
-
})
-
r; // true
4.3 some()
测试数组中的某些元素是否通过由提供的函数实现的测试。
语法:
arr.some(callback)
。
参数:
callback(val,index,arr)
: 需要执行的函数,接收三个参数:
-
val
: 正在处理的当前元素; -
index
: 可选,正在处理的当前元素的索引; -
arr
: 可选,正在操作的数组;
返回值:
若有一个通过返回 true
,否则返回 false
。
-
let a = [1, "", "aa", 13, 6];
-
let res = a.some(function(val, index, arr){
-
return typeof val == "number";
-
})
-
res;// true
-
let b = [1, 2, 3];
-
let r = b.some(function(val, index, arr){
-
return typeof val == "number";
-
})
-
r; // true
4.4 filter()
将所有在过滤函数中返回 true
的数组元素放进一个新数组中并返回。
语法:
arr.filter(callback)
。
参数:
callback(val,index,arr)
: 需要执行的函数,接收三个参数:
-
val
: 正在处理的当前元素; -
index
: 可选,正在处理的当前元素的索引; -
arr
: 可选,正在操作的数组;
返回值:
一个返回通过测试的元素的数组,若都没有则返回空数组。
-
let a = [1, "", "aa", 13, 6];
-
let res = a.filter(function(val, index, arr){
-
return typeof val == "number";
-
})
-
res;//[1, 13, 6]
4.5 map()
传入一个操作函数,对每个元素执行此方法,并返回一个执行后的数组。
语法:
arr.map(callback)
。
参数:
callback(val,index,arr)
: 需要执行的函数,接收三个参数:
-
val
: 正在处理的当前元素; -
index
: 可选,正在处理的当前元素的索引; -
arr
: 可选,正在操作的数组;
返回值:
一个新数组,每个元素都是回调函数的结果。
-
let a = [1, 3, 5];
-
let b = a.map(function(val, index, arr){
-
return val + 2;
-
})
-
b; //[3, 5, 7]
5. 数组的拓展(ES6)
5.1 拓展运算符
拓展运算符使用( ...
),类似 rest
参数的逆运算,将数组转为用( ,
)分隔的参数序列。
-
console.log(...[1, 2, 3]); // 1 2 3
-
console.log(1, ...[2,3], 4); // 1 2 3 4
拓展运算符主要使用在函数调用。
-
function f (a, b){
-
console.log(a, b);
-
}
-
f(...[1, 2]); // 1 2
-
function g (a, b, c, d, e){
-
console.log(a, b, c, d, e);
-
}
-
g(0, ...[1, 2], 3, ...[4]); // 0 1 2 3 4
若拓展运算符后面是个空数组,则不产生效果。
-
[...[], 1]; // [1]
替代apply方法
-
// ES6之前
-
function f(a, b, c){...};
-
var a = [1, 2, 3];
-
f.apply(null, a);
-
// ES6之后
-
function f(a, b, c){...};
-
let a = [1, 2, 3];
-
f(...a);
-
// ES6之前
-
Math.max.apply(null, [3,2,6]);
-
// ES6之后
-
Math.max(...[3,2,6]);
拓展运算符的运用
- (1)复制数组:
通常我们直接复制数组时,只是浅拷贝,如果要实现深拷贝,可以使用拓展运算符。
-
// 通常情况 浅拷贝
-
let a1 = [1, 2];
-
let a2 = a1;
-
a2[0] = 3;
-
console.log(a1,a2); // [3,2] [3,2]
-
// 拓展运算符 深拷贝
-
let a1 = [1, 2];
-
let a2 = [...a1];
-
// let [...a2] = a1; // 作用相同
-
a2[0] = 3;
-
console.log(a1,a2); // [1,2] [3,2]
- (2)合并数组:
注意,这里合并数组,只是浅拷贝。
-
let a1 = [1,2];
-
let a2 = [3];
-
let a3 = [4,5];
-
// ES5
-
let a4 = a1.concat(a2, a3);
-
// ES6
-
let a5 = [...a1, ...a2, ...a3];
-
a4[0] === a1[0]; // true
-
a5[0] === a1[0]; // true
- (3)与解构赋值结合:
与解构赋值结合生成数组,但是使用拓展运算符需要放到参数最后一个,否则报错。
-
let [a, ...b] = [1, 2, 3, 4];
-
// a => 1 b => [2,3,4]
-
let [a, ...b] = [];
-
// a => undefined b => []
-
let [a, ...b] = ["abc"];
-
// a => "abc" b => []
5.2 Array.from()
将 类数组对象 和 可遍历的对象,转换成真正的数组。
-
// 类数组对象
-
let a = {
-
'0':'a',
-
'1':'b',
-
length:2
-
}
-
let arr = Array.from(a);
-
// 可遍历的对象
-
let a = Array.from([1,2,3]);
-
let b = Array.from({length: 3});
-
let c = Array.from([1,2,3]).map(x => x * x);
-
let d = Array.from([1,2,3].map(x => x * x));
5.3 Array.of()
将一组数值,转换成数组,弥补 Array
方法参数不同导致的差异。
-
Array.of(1,2,3); // [1,2,3]
-
Array.of(1).length; // 1
-
Array(); // []
-
Array(2); // [,] 1个参数时,为指定数组长度
-
Array(1,2,3); // [1,2,3] 多于2个参数,组成新数组
5.4 find()和findIndex()
find()
方法用于找出第一个符合条件的数组成员,参数为一个回调函数,所有成员依次执行该回调函数,返回第一个返回值为 true
的成员,如果没有一个符合则返回 undefined
。
-
[1,2,3,4,5].find( a => a < 3 ); // 1
回调函数接收三个参数,当前值、当前位置和原数组。
-
[1,2,3,4,5].find((value, index, arr) => {
-
// ...
-
});
findIndex()
方法与 find()
类似,返回第一个符合条件的数组成员的位置,如果都不符合则返回 -1
。
-
[1,2,3,4].findIndex((v,i,a)=>{
-
return v>2;
-
}); // 2
5.5 fill()
用于用指定值填充一个数组,通常用来初始化空数组,并抹去数组中已有的元素。
-
new Array(3).fill('a'); // ['a','a','a']
-
[1,2,3].fill('a'); // ['a','a','a']
并且 fill()
的第二个和第三个参数指定填充的起始位置和结束位置。
-
[1,2,3].fill('a',1,2);// [1, "a", 3]
5.6 entries(),keys(),values()
主要用于遍历数组, entries()
对键值对遍历, keys()
对键名遍历, values()
对键值遍历。
-
for (let i of ['a', 'b'].keys()){
-
console.log(i)
-
}
-
// 0
-
// 1
-
for (let e of ['a', 'b'].values()){
-
console.log(e)
-
}
-
// 'a'
-
// 'b'
-
for (let e of ['a', 'b'].entries()){
-
console.log(e)
-
}
-
// 0 'a'
-
// 1 'b'
5.7 includes()
用于表示数组是否包含给定的值,与字符串的 includes
方法类似。
-
[1,2,3].includes(2); // true
-
[1,2,3].includes(4); // false
-
[1,2,NaN].includes(NaN); // true
第二个参数为起始位置,默认为 0
,如果负数,则表示倒数的位置,如果大于数组长度,则重置为 0
开始。
-
[1,2,3].includes(3,3); // false
-
[1,2,3].includes(3,4); // false
-
[1,2,3].includes(3,-1); // true
-
[1,2,3].includes(3,-4); // true
5.8 flat(),flatMap()
flat()
用于将数组一维化,返回一个新数组,不影响原数组。
默认一次只一维化一层数组,若需多层,则传入一个整数参数指定层数。
若要一维化所有层的数组,则传入 Infinity
作为参数。
-
[1, 2, [2,3]].flat(); // [1,2,2,3]
-
[1,2,[3,[4,[5,6]]]].flat(3); // [1,2,3,4,5,6]
-
[1,2,[3,[4,[5,6]]]].flat('Infinity'); // [1,2,3,4,5,6]
flatMap()
是将原数组每个对象先执行一个函数,在对返回值组成的数组执行 flat()
方法,返回一个新数组,不改变原数组。
flatMap()
只能展开一层。
-
[2, 3, 4].flatMap((x) => [x, x * 2]);
-
// [2, 4, 3, 6, 4, 8]
6. 数组的拓展(ES7)
6.1 Array.prototype.includes()方法
includes()
用于查找一个值是否在数组中,如果在返回 true
,否则返回 false
。
-
['a', 'b', 'c'].includes('a'); // true
-
['a', 'b', 'c'].includes('d'); // false
includes()
方法接收两个参数,搜索的内容和开始搜索的索引,默认值为0,若搜索值在数组中则返回 true
否则返回 false
。
-
['a', 'b', 'c', 'd'].includes('b'); // true
-
['a', 'b', 'c', 'd'].includes('b', 1); // true
-
['a', 'b', 'c', 'd'].includes('b', 2); // false
与 indexOf
方法对比,下面方法效果相同:
-
['a', 'b', 'c', 'd'].indexOf('b') > -1; // true
-
['a', 'b', 'c', 'd'].includes('b'); // true
includes()与indexOf对比:
-
includes
相比 indexOf
更具语义化, includes
返回的是是否存在的具体结果,值为布尔值,而 indexOf
返回的是搜索值的下标。 -
includes
相比 indexOf
更准确, includes
认为两个 NaN
相等,而 indexOf
不会。
-
let a = [1, NaN, 3];
-
a.indexOf(NaN); // -1
-
a.includes(NaN); // true
另外在判断 +0
与 -0
时, includes
和 indexOf
的返回相同。
-
[1, +0, 3, 4].includes(-0); // true
-
[1, +0, 3, 4].indexOf(-0); // 1
参考资料
1.MDN 索引集合类
2.MDN 数组对象
3.W3school JavaScript Array 对象