Array对象及原型

JavaScript的 Array 对象是用于构造数组的全局对象,数组是类似于列表的高阶对象。本文将主要讲解 Array.prototype 下的方法

fill

用一个固定值填充一个数组中从起始索引到终止索引内的全部元素,不包括终止索引。

语法:
arr.fill(value[, start[, end]])
value
用来填充数组元素的值。
start
起始索引,默认值为 0。
end
终止索引,默认值为 this.length。

1
2
3
4
5
6
new Array(5).fill(1); // [1, 1, 1, 1,1]

var array1 = [1, 2, 3, 4];
// fill with 0 from position 2 until position 4
console.log(array1.fill(0, 2, 4));
// expected output: [1, 2, 0, 0]

splice

可删除或添加新的元素来修改原数组,返回被删除的元素组成的一个数组,如果没有删除元素,则返回空数组。

array.splice(start[, deleteCount[, item1[, item2[, …]]]])
start
指定修改的开始位置
deleteCount
要移除的数组元素的个数
item1, item2, ...
从start 位置开始,添加进数组的元素

1
2
3
4
var arr = [1, 2, 3, 4]
arr.splice(2, 1, 0)
// arr: [1, 2, 0, 4]
// 被删除的元素: [3]

copyWithin

浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
arr.copyWithin(target[, start[, end]])
target
复制到该位置的索引
start
可选,开始复制元素的起始位置
end
可选,开始复制元素的结束位置

1
2
3
4
5
var arr = [1, 2, 3, 4, 5];
arr.copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
arr.copyWithin(-2)
// [1, 2, 3, 1, 2]

some

测试是否至少有一个元素可以符合条件,也就是被提供的函数返回true。some() 为数组中的每一个元素执行一次 callback 函数,直到找到一个使得 callback 返回一个“真值”(即可转换为布尔值 true 的值)。如果找到了这样一个值,some() 将会立即返回 true,否则,都不符合条件,返回 false。

1
2
[1, 2, 3, 0].some(v => !v)
// true

every

跟 some 不一样,every 检测一个数组内的所有元素是否都能通过某个指定函数的测试,并返回布尔值。如果某个元素使 callback 返回 false,则循环终止,every 立即返回false,否则,返回 true。

注意:空数组情况下返回true。every 和数学中的"所有"类似,当所有的元素都符合条件才会返回true。正因如此,若传入一个空数组,无论如何都会返回 true。(这种情况属于无条件正确:正因为一个空集合没有元素,所以它其中的所有元素都符合给定的条件。)

1
2
[1, 2, 3, 0].every(v => !v)
// false

includes

用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回false。

注意:对象数组不能使用includes方法来检测。

1
2
3
4
[1, 2, 3].includes(2);
// true
[1, 2, NaN].includes(NaN);
// true

map

map 方法会循环遍历数组,对每一个元素进行函数式的处理。注意:原来数组不会发生变化,会返回一个新的数组。

比如有这样一个函数f(x)=x*x,用 map 实现如下:

1
2
3
4
5
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

arr.map(function(x) {
return x * x;
});

运行结果:[1, 4, 9, 16, 25, 36, 49, 64, 81]

此功能完全可以自己封装一个函数来完成,但是不容易一眼就看出是函数作用于元素,也就是可读性不高。map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以f(x)=x*x,还可以计算任意复杂的函数。

此方法还可以转换数组元素类型:

1
2
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(String);

运行结果: [“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”]

把字符串转化为数组:

1
2
3
Array.prototype.map.call("String", function(x) {
return x;
});

运行结果:[“S”, “t”, “r”, “i”, “n”, “g”]

map 和 forEach 异同点:

  • map 和 forEach 非常相似,都是用来遍历数组中的每一项,并不对原数组进行修改;
  • 区别在于 map 支持 return 返回(返回的是进行修改后的原数组的克隆版本),而 forEach 不支持;
  • 两者都支持第二个参数,这第二个参数的意思是把匿名回调函数中的 this 进行修改。
1
2
3
4
var obj = { key: 12 };
[1, 2, 3].map(function(x) {
return x * this.key;
}, obj);

运行结果:[12, 24, 36]


reduce

直接上代码,看看运行结果:

1
2
3
4
5
6
var arr = [1, 2, 3, 4, 5];

sum = arr.reduce(function(res, cur, index, self) {
console.log(res, cur, index);
return res + cur;
});

运行结果:

1
2
3
4
5
1 2 1
3 3 2
6 4 3
10 5 4
15 // reduce回调函数返回值

reduce回调函数中有四个参数:

res: 第一项的值或者上一次叠加的结果值
cur: 当前会参与叠加的项
index:当前参与叠加项的索引
self: 数组本身

从这个例子可以看出reduce能实现累加运算,那么能进行累积运算吗,当然可以,这个自己下去试试就知道了。

给 reduce 参数添加第二个参数,通过打印我发现,reduce会遍历 5 次。

1
2
3
4
5
6
var arr = [1, 2, 3, 4, 5];

sum = arr.reduce(function(res, cur, index, self) {
console.log(res, cur, index);
return res + cur;
}, 0);

运行结果:

1
2
3
4
5
6
0 1 0
1 2 1
3 3 2
6 4 3
10 5 4
15 // reduce回调函数返回值

这第二个参数就是设置 res 的数据类型和初始值,设置为 0,就表示 res 的初始值为number类型,值为 0,因此,reduce的最终结果也会是number类型。

那么,如何计算一串字符串中每个字母出现的次数呢?

1
2
3
4
5
6
var str = "abcdaabce";

str.split("").reduce(function(res, cur) {
res[cur] ? res[cur]++ : (res[cur] = 1);
return res;
}, {});

运行结果:{a: 3, b: 2, c: 2, d: 1, e: 1}

由于可以通过第二个参数设置叠加结果的类型和初始值,我们可以灵活的运用它来进行各种各样的类型转换,比如将数组按照一定规则转换为对象,也可以将一种形式的数组转换为另一种形式的数组。

这种特性使得reduce在实际开发中大有可为!但是需要注意点,在 IE9 以下的浏览器中,并不支持该方法!

应用:

  1. reduce还可以去除字符串中的重复字符,类似于数组去重
1
2
3
4
5
6
var str = "abc12122adc";

str.split("").reduce(function(res, cur, i, self) {
self.indexOf(cur) === i ? res.push(cur) : null;
return res;
}, []);
  1. 递归方法将多维数组转一维:
1
2
3
4
5
6
7
8
9
var arr = [1, [2, 3, 4], [[5, 6], 7], 8];
const flatten = arr =>
arr.reduce(
(res, cur) => res.concat(Array.isArray(cur) ? flatten(cur) : cur),
[]
);

// 注意:ES9中将新增flat方法,flat()方法会递归到指定深度将所有子数组连接,并返回一个新数组。例:
arr.flat(Infinity); // [1,2,3,4,5,6,7,8,]
  1. 将树形json结构扁平化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// reduce 第二个参数为当前参与计算的项,也就是数组的某个元素 (Object)
// 参数里的 { name } 为es6中解构赋值的特性,下面的{ name } 相当于 { name: name }
function flatten(data) {
return data.reduce(
(arr, { name, children = [] }) => arr.concat([{ name }], flatten(children)),
[]
);
}

// Result:
[
{ name: "Juery" },
{ name: "LiLy" },
{ name: "Shyane" },
{ name: "Taylor" },
{ name: "Choy" },
{ name: "Fusion" },
{ name: "Simon" },
{ name: "Sunshine" },
{ name: "Meton" },
{ name: "Adele" },
];

Json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[
{
"name": "Juery",
"children": [
{
"name": "LiLy",
"children": [
{
"name": "Shyane"
},
{
"name": "Taylor"
}
]
},
{
"name": "Choy"
}
]
},
{
"name": "Fusion"
},
{
"name": "Simon",
"children": [
{
"name": "Sunshine",
"children": [
{
"name": "Meton"
},
{
"name": "Adele"
}
]
}
]
}
]

filter

顾名思义,它用于过滤某些元素,返回剩下的元素。和 map()有点类似,filter()也接收一个函数。和 map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是 true 还是 false 决定保留还是丢弃该元素。

1
2
3
4
5
6
7
8
var s = "i love you very very very much,do you love me?";

s.slice(0, -1)
.split(/[\s\,]+/)
.filter(function(ele, index, self) {
// slice去掉末尾'?'
return self.indexOf(ele) === index;
});

运行结果:[“i”, “love”, “you”, “very”, “much”, “do”, “me”]

ele: 当前遍历的数组元素
index:当前遍历元素的位置
self: 数组本身

由此可看出filterreduce的去重效果是一样的,不过这种方法更简洁,逻辑更清晰。

其实还有更简便的方法:

1
2
3
Array.from(new Set(arr)); //ES6
[...new Set(arr)];
Array.from(arr); //注意: 此数组方法可将伪数组转真数组

参考文档:Array 方法