重温基础封面


Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推荐 https://github.com/pingan8787/Leo_Reading/issues

本章节复习的是JS中的数组,以索引进行排序。

前置知识:
数组是一个有序的数据集合,使用数组名称和索引进行访问。

1
2
let arr = [1,2,3];
arr[0] = 1;

在JavaScript中数组没有明确数据类型。

1
let arr = [1, 'hi', undefined, fun()];

1.创建数组

创建数组方法有3种:

1
2
3
let arr = new Array(ele1, ele2, ele3, ..., eleN);
let arr = Array(ele1, ele2, ele3, ..., eleN);
let arr = [ele1, ele2, ele3, ..., eleN];

上面是已知数组元素,另外一种还有创建一个长度不为0,且有没有任何元素的数组

1
2
3
4
5
6
let len = 5;

let arr = new Array(len); // 方法1
let arr = Array(len); // 方法2
let arr = []; // 方法3
arr.length = len;

若传入的数组长度不是整数,则报错:

1
2
3
4
5
let arr = new Array(3.5); 
let arr = Array(3.5);
let arr = [];
arr.length = 3.5;
//Uncaught RangeError: Invalid array length

其中要注意这两种创建方法是不同的:

1
2
3
4
5
6
7
8
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 简单使用

获取数组指定位置的值:

1
2
3
4
5
6
7
8
9
10
11
let a = [1,2,5];
a[0]; // 1
a[2]; // 5
a[3]; // undefined
```

获取数组长度:
```js
let a = [1,2,5];
a.length; // 3
a["length"]; // 3

设置数组指定位置的值:

1
2
3
4
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循环:

    1
    2
    3
    4
    5
    6
    7
    8
    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

    1
    2
    3
    4
    5
    6
    7
    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 的详细介绍。

1
2
3
4
let arr = ["pingan", "leo", "robin"];
arr.forEach(function(val, i, array){
console.log(`第${i}个元素是:${val};`)
})

3. 数组方法(访问和修改)

方法名称 方法介绍
concat() 连接两个或更多的数组,并返回结果。
join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
pop() 删除并返回数组的最后一个元素
push() 向数组的末尾添加一个或更多元素,并返回新的长度。
reverse() 颠倒数组中元素的顺序。
shift() 删除并返回数组的第一个元素
slice() 从某个已有的数组返回选定的元素
sort() 对数组的元素进行排序
splice() 删除元素,并向数组添加新元素。
toSource() 返回该对象的源代码。
toString() 把数组转换为字符串,并返回结果。
toLocaleString() 把数组转换为本地数组,并返回结果。
unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
valueOf() 返回数组对象的原始值
indexOf() 在数组中搜索指定元素并返回第一个匹配的索引
lastIndexOf() 在数组中搜索指定元素并返回最后一个匹配的索引

可参考W3school JavaScript Array 对象 的详细介绍。

3.1 concat()

连接两个或更多的数组,并返回一个新数组。

  • 语法:
    arr.concat(a1, a2, ..., an);
  • 参数:
    arr:目标数组;
    a1,a2,...,an:需要合并的元素;
    1
    2
    3
    4
    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:连接的分隔符,默认值为“,”;
    1
    2
    3
    4
    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()是向最后一位添加新的元素。

    1
    2
    3
    4
    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()是向第一位添加新的元素。

1
2
3
4
let arr = ["pingan", "leo"];
let a1 = arr.shift(); // "pingan"
let a2 = arr.unshift("robin","hi"); // 3
arr; // ["robin", "hi", "leo"]

3.5 reverse()

颠倒数组中元素的顺序,改变原数组

1
2
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索引上的值。

1
2
3
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:可选,为数组添加的元素;
    1
    2
    3
    4
    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()

对数组的元素进行排序,改变原数组
可接受一个回调方法作为比较函数,来决定排序方式。
比较函数应该具有两个参数 ab,返回值如下:
a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
a 等于 b,则返回 0。
a 大于 b,则返回一个大于 0 的值。

1
2
3
4
5
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: 开始搜索的索引。
    1
    2
    3
    4
    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. 数组方法(迭代)

方法名称 方法介绍
forEach() 为数组中的每个元素执行一次回调函数。
every() 如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false。
some() 如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。
filter() 将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回。
map() 返回一个由回调函数的返回值组成的新数组。
reduce() 从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。
reduceRight() 从右到左为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。

以下是ES6规范新增的数组方法:

方法名称 方法介绍
keys() 返回一个数组迭代器对象,该迭代器会包含所有数组元素的键。
values() 返回一个数组迭代器对象,该迭代器会包含所有数组元素的值。
entries() 返回一个数组迭代器对象,该迭代器会包含所有数组元素的键值对。
find() 找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined。
findIndex() 找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1。

可参考MDN Array 的详细介绍。

4.1 forEach()

对数组的每个元素执行一次提供的函数。

语法:
arr.forEach(callback)

参数:
callback(val, index, arr) : 需要执行的函数,接收三个参数:

  • val : 正在处理的当前元素;
  • index : 可选,正在处理的当前元素的索引;
  • arr : 可选,正在操作的数组;
1
2
3
4
5
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

1
2
3
4
5
6
7
8
9
10
11
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

1
2
3
4
5
6
7
8
9
10
11
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 : 可选,正在操作的数组;

返回值:
一个返回通过测试的元素的数组,若都没有则返回空数组。

1
2
3
4
5
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 : 可选,正在操作的数组;

返回值:
一个新数组,每个元素都是回调函数的结果。

1
2
3
4
5
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参数的逆运算,将数组转为用(,)分隔的参数序列。

1
2
console.log(...[1, 2, 3]);   // 1 2 3 
console.log(1, ...[2,3], 4); // 1 2 3 4

拓展运算符主要使用在函数调用。

1
2
3
4
5
6
7
8
9
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]; // [1]

替代apply方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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)复制数组
    通常我们直接复制数组时,只是浅拷贝,如果要实现深拷贝,可以使用拓展运算符。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 通常情况 浅拷贝
    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)合并数组
    注意,这里合并数组,只是浅拷贝。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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)与解构赋值结合
    与解构赋值结合生成数组,但是使用拓展运算符需要放到参数最后一个,否则报错。

    1
    2
    3
    4
    5
    6
    7
    8
    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()

类数组对象可遍历的对象,转换成真正的数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 类数组对象
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方法参数不同导致的差异。

1
2
3
4
5
6
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
[1,2,3,4,5].find( a => a < 3 ); // 1

回调函数接收三个参数,当前值、当前位置和原数组。

1
2
3
[1,2,3,4,5].find((value, index, arr) => {
// ...
});

findIndex()方法与find()类似,返回第一个符合条件的数组成员的位置,如果都不符合则返回-1

1
2
3
[1,2,3,4].findIndex((v,i,a)=>{
return v>2;
}); // 2

5.5 fill()

用于用指定值填充一个数组,通常用来初始化空数组,并抹去数组中已有的元素。

1
2
new Array(3).fill('a');   // ['a','a','a']
[1,2,3].fill('a'); // ['a','a','a']

并且fill()的第二个和第三个参数指定填充的起始位置结束位置

1
[1,2,3].fill('a',1,2);//  [1, "a", 3]

5.6 entries(),keys(),values()

主要用于遍历数组,entries()对键值对遍历,keys()对键名遍历,values()对键值遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
[1,2,3].includes(2);     // true
[1,2,3].includes(4); // false
[1,2,NaN].includes(NaN); // true

第二个参数为起始位置,默认为0,如果负数,则表示倒数的位置,如果大于数组长度,则重置为0开始。

1
2
3
4
[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
3
[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()只能展开一层。

1
2
[2, 3, 4].flatMap((x) => [x, x * 2]); 
// [2, 4, 3, 6, 4, 8]

6. 数组的拓展(ES7)

6.1 Array.prototype.includes()方法

includes()用于查找一个值是否在数组中,如果在返回true,否则返回false

1
2
['a', 'b', 'c'].includes('a');     // true
['a', 'b', 'c'].includes('d'); // false

includes()方法接收两个参数,搜索的内容开始搜索的索引,默认值为0,若搜索值在数组中则返回true否则返回false

1
2
3
['a', 'b', 'c', 'd'].includes('b');      // true
['a', 'b', 'c', 'd'].includes('b', 1); // true
['a', 'b', 'c', 'd'].includes('b', 2); // false

indexOf方法对比,下面方法效果相同:

1
2
['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不会。
    1
    2
    3
    let a = [1, NaN, 3];
    a.indexOf(NaN); // -1
    a.includes(NaN); // true

另外在判断+0-0时,includesindexOf的返回相同。

1
2
[1, +0, 3, 4].includes(-0);   // true
[1, +0, 3, 4].indexOf(-0); // 1

参考资料

1.MDN 索引集合类
2.MDN 数组对象
3.W3school JavaScript Array 对象


本部分内容到这结束

Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推荐 https://github.com/pingan8787/Leo_Reading/issues
JS小册 js.pingan8787.com