【重温基础】10.数组

本文是 重温基础 系列文章的第十篇。
今日感受:平安夜,多棒。

系列目录:

本章节复习的是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. 数组方法(访问和修改)

方法名称 方法介绍
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:需要合并的元素;
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()

对数组的元素进行排序,改变原数组
可接受一个回调方法作为比较函数,来决定排序方式。
比较函数应该具有两个参数 ab,返回值如下:
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. 数组方法(迭代)

方法名称 方法介绍
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 : 可选,正在操作的数组;
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时,includesindexOf的返回相同。

[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
微信公众号【前端自习课】
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • 转载:在开发中,数组的使用场景非常多,平日中也涉及到很多数组的api/相关操作,一直也没有对这块内容进行一块整理总...
    七色烟火阅读 3,207评论 0 3
  • Javascript有很多数组的方法,有的人有W3C的API,还可以去MDN上去找,但是我觉得API上说的不全,M...
    顽皮的雪狐七七阅读 4,063评论 0 6
  • The JavaScriptArrayobject is a global object thatis used ...
    skycolor阅读 559评论 0 0
  • 总目录 上一章 湾陆医院8楼。 中午时分。依旧没有见到边婷的影子。龙华生、吴凯急的团团转。跟护士要人吗?那个当...
    委子阅读 266评论 2 6
  • 在iOS中框架是一个目录,包含了共享资源库,用于访问该资源库中储存的代码的头文件,以及图像、声音文件等其他资源。共...
    ch123阅读 1,764评论 0 1