◆

JavaScript 数组 Array 方法大全与用法详解

2023-02-02 01:370

什么是数组

  • 是按次序排列的一组值。每个值的位置都有编号(从 0 开始),整个数组用方括号表示。
  • 数组是值得有序集合,每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引。
  • js 的数组元素可以是任意类型,同一个数组中的不同元素可能是对象或数组。
  • 数组元素的索引不一定要连续,元素之间可以有空隙,叫做稀疏数组 。
  • 每个数组都具有一个 length 属性。
  • 针对非稀疏数组,length 属性就是数组元素的个数,针对稀疏数组,元素的 length 属性比所有元素的索引要大。

创建数组

推荐使用这种方式,性能更高

var arr1 = [];
var arr2 = [1, 3, 45];
var arr3 = [1, 'aa', true]; // 有三个不同数据类型的数组,最后一个逗号为结尾的逗号

调用构造函数 Array() 创建数组 不推荐使用这种方式

var a = new Array(); // [] 调用时没有参数
var b = new Array(10); // 调用时有一个数值参数,它指定长度
var c = new Array(1, 2, 3); // [1, 2, 3] 显式指定两个或多个数组元素或者数组的一个非数值元素
var d = new Array('22'); // ['22'] 如果传入一个非数值的参数或者参数个数大于1,则表示创建一个包含指定元素的数组

数组元素的读和写

使用 [] 操作符来访问数组中的一个元素

var c = [0, , 2];
console.log(c[0]); // 0
console.log(c[1]); // undefined
console.log(c[2]); // 2
console.log(c[3]); // undefined
c[1] = 3;
console.log(c); // [0, 3, 2]
c[5] = 6;
console.log(c); // [0, 3, 2, empty × 2, 6]

数组的长度

  • 每个数组都有一个 length 属性,针对非稀疏数组,length 属性值代表数组中元素的个数,其值比数组中最大的索引大一。
  • 当数组是稀疏时,length 属性值会大于元素个数。数组的长度会大于每一个元素的索引值。
  • 该属性是一个动态的值,等于键名中的最大整数加上 1。
var c = [0, , 2];
console.log(c.length); // =>3 稀疏数组,最大索引值为2,数组长度为3

c[1000] = 10;
console.log(c.length); // 1001

length 属性是可写的。如果人为设置一个小于当前成员个数的值,该数组的成员数量会自动减少到 length 设置的值。

var arr = ['a', 'b', 'c'];
arr.length; // 3

arr.length = 2;
arr; // ["a", "b"]

上面代码表示,当数组的 length 属性设为 2(即最大的整数键只能是 1)那么整数键 2(值为 c)就已经不在数组中了,被自动删除了。

清空数组的一个有效方法,就是将 length 属性设为 0。

如果人为设置 length 大于当前元素个数,则数组的成员数量会增加到这个值,新增的位置都是空位。 当 length 属性设为大于数组个数时,读取新增的位置都会返回 undefined。

var a = ['a'];

a.length = 3;
a[1]; // undefined

由于数组本质上是一种对象,所以可以为数组添加属性,但是这不影响 length 属性的值。

因为,length 属性的值就是等于最大的数字键加 1,而这个数组没有整数键,所以 length 属性保持为 0。

var a = [];

a['p'] = 'abc';
a.length; // 0

a[2.1] = 'abc';
a.length; // 0

数组也是对象

数组通过数字进行索引,它们也是对象,所以也可以包含字符串键值和属性 (但这些并不计算在数组长度内)

  • 数组的键名是按次序排列的一组整数(0,1,2...)
  • 数组的键名其实也是字符串。之所以可以用数值读取,是因为非字符串的键名会被转为字符串。
typeof [1, 2, 3]; // "object"

var a = [];
a[0] = 1;
a['foobar'] = 2;
console.log(a); // [1, foobar: 2]
console.log(a.length); // 1
console.log(a['foobar']); // 2
console.log(a[0]); // 1
console.log(a[1]); // undefined

这里有个问题需要特别注意,如果字符串键值能够被强制类型转换为十进制数字的话,它就会被当作数字索引来处理。

var a = [];
a['13'] = 42;
a.length; // 14

数组的空位

当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位

var a = [1, , 1];
a.length; // 3

需要注意的是,如果最后一个元素后面有逗号,并不会产生空位

var a = [1, 2, 3];

a.length; // 3
a; // [1, 2, 3]

数组的空位是可以读取的,返回 undefined

var a = [, , ,];
a[1]; // undefined

使用 delete 命令删除一个数组成员,会形成空位,并且不会影响 length 属性。

var a = [1, 2, 3];
delete a[1];

a[1]; // undefined
a.length; // 3

上面代码用 delete 命令删除了数组的第二个元素,这个位置就形成了空位,但是对 length 属性没有影响。也就是说,length 属性不过滤空位。所以,使用 length 属性进行数组遍历,一定要非常小心。

空位与 undefined 的区别

数组的某个位置是空位,与某个位置是 undefined,是不一样的。如果是空位,使用数组的 forEach 方法、for...in 结构、以及 Object.keys 方法进行遍历,空位都会被跳过。

var a = [, , ,];

a.forEach(function (x, i) {
  console.log(i + '. ' + x);
});
// 不产生任何输出

for (var i in a) {
  console.log(i);
}
// 不产生任何输出

Object.keys(a);
// []

如果某个位置是 undefined,遍历的时候就不会被跳过。

var a = [undefined, undefined, undefined];

a.forEach(function (x, i) {
  console.log(i + '. ' + x);
});
// 0. undefined
// 1. undefined
// 2. undefined

for (var i in a) {
  console.log(i);
}
// 0
// 1
// 2

Object.keys(a);
// ['0', '1', '2']

空位就是数组没有这个元素,所以不会被遍历到,而 undefined 则表示数组有这个元素,值是 undefined,所以遍历不会跳过。

shift

删除原数组第一项,并返回删除元素的值;如果数组为空则返回 undefined

  • 执行后原数组会被改变
var a = [1, 2, 3, 4, 5];
var b = a.shift();
// a:[2, 3, 4, 5]  b:1

unshift

将参数添加到原数组开头,并返回数组的长度

  • 可添加多个
var a = [1, 2, 3, 4, 5];
var b = a.unshift(-2, -1);
// a:[-2, -1, 1, 2, 3, 4, 5] b:7

pop

删除原数组最后一项,并返回删除元素的值;如果数组为空则返回 undefined

var a = [1, 2, 3, 4, 5];
var b = a.pop();
// a:[1, 2, 3, 4] b:5

push

将参数添加到原数组末尾,并返回数组的长度

  • 可添加多个
var a = [1, 2, 3, 4, 5];
var b = a.push(6, 7);
// a:[1, 2, 3, 4, 5, 6, 7] b:7

concat

将多个数组拼接成一个数组,拼接后数组的元素将按照传入参数的顺序排序 ,不改变原数组,返回拼接后的数组

  • 如下面的示例 concat 会返回一个新数组,但是是对原数组的浅拷贝
var array = [1, 2, { name: 'wfly' }, { age: 18 }];
var newArray = array.concat([3, 4], 'a', true);
newArray[2].name = 'fnn';
array[3].age = 8;
console.log(array); // [1,2,{"name":"fnn"},{"age":8}]
console.log(newArray); // [1,2,{"name":"fnn"},{"age":8},3,4,"a",true]

splice

  • 语法:splice(start, deleteCount, val1, val2, ...)
  • 从 start 位置开始删除 deleteCount 项,并从该位置起插入 val1, val2,...
  • splice() 方法始终都会返回一个数组,该数组中包含从原始数组中删除的项,如果没有删除任何项,则返回一个空数组
var a = [1, 2, 3, 4, 5];
var b = a.splice(2, 2, 7, 8, 9); //a:[1, 2, 7, 8, 9, 5] b:[3, 4]
var b = a.splice(0, 1); // 同shift
a.splice(0, 0, -2, -1);
var b = a.length; // 同unshift
var b = a.splice(a.length - 1, 1); // 同pop
a.splice(a.length, 0, 6, 7);
var b = a.length; // 同push

reverse

将数组反序,改变原数组

var a = [1, 2, 3, 4, 5];
var b = a.reverse();
// a:[5, 4, 3, 2, 1] b:[5, 4, 3, 2, 1]

slice(start,end)

  • 返回从原数组中指定开始下标到结束下标之间的项组成的新数组。
  • 在只有一个参数的情况下, slice() 方法返回从该参数指定位置开始到当前数组末尾的所有项。
  • 如果有两个参数,该方法返回起始和结束位置之间的项——但不包括结束位置的项。
  • 当出现负数时,将负数加上数组长度的值来替换该位置的数(原数组中的倒数第几个元素开始提取),slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
var a = [1, 2, 3, 4, 5];
var b = a.slice(2, 5);
// a:[1, 2, 3, 4, 5];  b:[3, 4, 5]
  • slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。
  • 你只需将该方法绑定到这个对象上。 一个函数中的 arguments 就是一个类数组对象的例子
function fxFn() {
  return Array.prototype.slice.call(arguments);
}

var fx = fxFn(1, 2, 3);
console.log(fx); // [1, 2, 3]

除了使用 Array.prototype.slice.call(arguments),你也可以简单的使用 [].slice.call(arguments) 来代替。另外,你可以使用 bind 来简化该过程。

var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);

function fxFn() {
  return slice(arguments);
}

var fx = fxFn(1, 2, 3);
console.log(fx); // [1, 2, 3]

join(separator)

将数组的元素组起一个字符串,以 separator 为分隔符,省略的话则用默认用逗号为分隔符

var a = [1, 2, 3, 4, 5];
var b = a.join('|');
// a:[1, 2, 3, 4, 5] b:"1|2|3|4|5"

sort()

  • 按升序排列数组项——即最小的值位于最前面,最大的值排在最后面
  • 在排序时,sort()方法会调用每个数组项的 toString() 转型方法,然后比较得到的字符串,以确定如何排序。
  • 即使数组中的每一项都是数值, sort() 方法比较的也是字符串,因此会出现以下的这种情况
var arr1 = ['a', 'd', 'c', 'b'];
console.log(arr1.sort()); // ["a", "b", "c", "d"]
arr2 = [13, 24, 51, 3];
console.log(arr2.sort()); // [13, 24, 3, 51]
console.log(arr2); // [13, 24, 3, 51](元数组被改变)

为了解决上述问题,sort() 方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。 比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回 0,如果第一个参数应该位于第二个之后则返回一个正数。以下就是一个简单的比较函数:

function compare(value1, value2) {
  if (value1 < value2) {
    return -1;
  } else if (value1 > value2) {
    return 1;
  } else {
    return 0;
  }
}

arr2 = [13, 24, 51, 3];
console.log(arr2.sort(compare)); // [3, 13, 24, 51]

如果需要通过比较函数产生降序排序的结果,只要交换比较函数返回的值即可:

function compare(value1, value2) {
  if (value1 < value2) {
    return 1;
  } else if (value1 > value2) {
    return -1;
  } else {
    return 0;
  }
}

arr2 = [13, 24, 51, 3];
console.log(arr2.sort(compare)); // [51, 24, 13, 3]

indexOf & lastIndexOf

  • indexOf:接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, 从数组的开头(位置 0)开始向后查找。
  • lastIndexOf:接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, 从数组的末尾开始向前查找。

这两个方法都返回要查找的项在数组中的位置,或者在没找到的情况下返回 -1。在比较第一个参数与数组中的每一项时,会使用全等操作符。

var array = [1, 2, 3, 4, 5];
var ret = array.indexOf(4);
console.log(array); // [1, 2, 3, 4, 5]
console.log(ret); // 3
console.log(array.indexOf(6)); // -1
console.log(array.lastIndexOf(8)); // -1
console.log(array.lastIndexOf(5)); // 4

copyWithin(target,staart, end)

  • 浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
  • target:0 为基底的索引,复制序列到该位置。如果是负数,target 将从末尾开始计算。
  • 如果 target 大于等于 arr.length,将会不发生拷贝。如果 target 在 start 之后,复制的序列将被修改以符合 arr.length。
  • start:0 为基底的索引,开始复制元素的起始位置。
  • 如果 start 被忽略,copyWithin 将会从 0 开始复制。
  • end:0 为基底的索引,开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。
  • 如果 end 被忽略,copyWithin 方法将会一直复制至数组结尾(默认为 arr.length)。
  • 如果 start 为负,则其指定的索引位置等同于 length+start,length 为数组的长度。end 也是如此。
let numbers = [1, 2, 3, 4, 5];

numbers.copyWithin(-2);
// [1, 2, 3, 1, 2]

numbers.copyWithin(0, 3);
// [4, 5, 3, 4, 5]

numbers.copyWithin(0, 3, 4);
// [4, 2, 3, 4, 5]

numbers.copyWithin(-2, -3, -1);
// [1, 2, 3, 3, 4]

[].copyWithin.call({ length: 5, 3: 1 }, 0, 3);
// {0: 1, 3: 1, length: 5}

// ES2015 Typed Arrays are subclasses of Array
var i32a = new Int32Array([1, 2, 3, 4, 5]);

i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]

// On platforms that are not yet ES2015 compliant:
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]

fill(value,start,end)

  • 用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
  • value:用来填充数组元素的值。
  • start :可选,起始索引,默认值为 0。
  • end :可选,终止索引,默认值为 this.length。
  • 返回值:修改后的数组
  • 如果 start 是个负数, 则开始索引会被自动计算成为 length+start, 其中 length 是 this 对象的 length 属性值。
  • 如果 end 是个负数, 则结束索引会被自动计算成为 length+end。
[1, 2, 3].fill(4); // [4, 4, 4]
[1, 2, 3].fill(4, 1); // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2); // [1, 4, 3]
[1, 2, 3].fill(4, 1, 1); // [1, 2, 3]
[1, 2, 3].fill(4, 3, 3); // [1, 2, 3]
[1, 2, 3].fill(4, -3, -2); // [4, 2, 3]
[1, 2, 3].fill(4, NaN, NaN); // [1, 2, 3]
[1, 2, 3].fill(4, 3, 5); // [1, 2, 3]
Array(3).fill(4); // [4, 4, 4]
[].fill.call({ length: 3 }, 4); // {0: 4, 1: 4, 2: 4, length: 3}

// Objects by reference.
var arr = Array(3).fill({}); // [{}, {}, {}];
// 需要注意如果fill的参数为引用类型,会导致都执行都一个引用类型
// 如 arr[0] === arr[1] 为true
arr[0].hi = 'hi'; // [{ hi: "hi" }, { hi: "hi" }, { hi: "hi" }]

includes(valueToFind,fromIndex)

  • 判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false。
  • valueToFind:需要查找的元素值。
  • fromIndex:可选,从fromIndex 索引处开始查找 valueToFind。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。默认为 0。
  • 如果 fromIndex 为负值,计算出的索引将作为开始搜索 searchElement 的位置。如果计算出的索引小于 0,则整个数组都会被搜索。
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
[1, 2, NaN].includes(NaN); // true

var arr = ['a', 'b', 'c'];

arr.includes('a', -100); // true
arr.includes('b', -100); // true
arr.includes('c', -100); // true
arr.includes('a', -2); // false
  • 作为通用方法的 includes()
(function () {
  console.log([].includes.call(arguments, 'a')); // true
  console.log([].includes.call(arguments, 'd')); // false
})('a', 'b', 'c');

keys() / values() / entries()

返回一个包含数组中每个索引键的 Array Iterator 对象。索引迭代器会包含那些没有对应元素的索引

var arr = ['a', , 'c'];
var sparseKeys = Object.keys(arr);
console.log(sparseKeys); // ['0', '2']
[...arr1.entries()]; // [[0, "a"][1, undefined][2, "c"]]
[...arr1.keys()]; // [0, 1, 2]
[...arr1.values()]; // ["a", undefined, "c"]

filter()

创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。

  • 返回值:一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var arr2 = arr.filter(function (x, index) {
  return index % 3 === 0 || x >= 8;
});
console.log(arr2); //[1, 4, 7, 8, 9, 10]
var arr = [1, 3, 4, 7, 12];
var arrTest = arr.filter((item, index, array) => {
  console.log('当前值:' + item, '当前值的索引' + index, '当前数组' + array);
  return item >= 4;
});
console.log(arrTest);

every()

every():判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回 true。

var arr = [1, 2, 3, 4, 5];
var arr2 = arr.every(function (x) {
  return x < 10;
});
console.log(arr2); //true
var arr3 = arr.every(function (x) {
  return x < 3;
});
console.log(arr3); // false
var arr = [1, 3, 4, 7, 12];
var arrTest = arr.every((item, index, array) => {
  console.log('当前值:' + item, '当前值的索引' + index, '当前数组' + array);
  return item > 0;
});
console.log(arrTest);

some()

some():判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回 true。

var arr = [1, 2, 3, 4, 5];
var arr2 = arr.some(function (x) {
  return x < 3;
});
console.log(arr2); //true
var arr3 = arr.some(function (x) {
  return x < 1;
});
console.log(arr3); // false

Array.from()

  • 将伪数组对象(拥有一个 length 属性和若干索引属性的任意对象)或迭代对象(可以获取对象中的元素,如 Map 和 Set 等)转化成真数组,返回值:一个新的数组
  • Array.from(arrayLike, mapFn, thisArg)
    • arrayLike 想要转换成数组的伪数组对象或可迭代对象。
    • mapFn (可选参数) 如果指定了该参数,新数组中的每个元素会执行该回调函数。
    • thisArg (可选参数) 可选参数,执行回调函数 mapFn 时 this 对象。
Array.from('foo');
// ["f", "o", "o"]

// =====

let s = new Set(['foo', window]);
Array.from(s);
// ["foo", window]

// =====

let m = new Map([
  [1, 2],
  [2, 4],
  [4, 8],
]);
Array.from(m);
// [[1, 2], [2, 4], [4, 8]]

const mapper = new Map([
  ['1', 'a'],
  ['2', 'b'],
]);
Array.from(mapper.values());
// ['a', 'b'];

Array.from(mapper.keys());
// ['1', '2'];

// =====

function f() {
  return Array.from(arguments);
}

f(1, 2, 3); // [1, 2, 3]

// =====

Array.from([1, 2, 3], x => x + x);
// x => x + x代表这是一个函数,只是省略了其他的定义,这是一种Lambda表达式的写法
// 箭头的意思表示从当前数组中取出一个值,然后自加,并将返回的结果添加到新数组中
// [2, 4, 6]
Array.from({ length: 5 }, (v, i) => i);
// [0, 1, 2, 3, 4]
  • 数组去重合并
function combine() {
  let arr = [].concat.apply([], arguments); //没有去重复的新数组
  return Array.from(new Set(arr));
}

var m = [1, 2, 2],
  n = [2, 3, 3];
console.log(combine(m, n)); // [1, 2, 3]

Array.of()

  • 创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型
  • Array.of() 和 Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,
  • 而 Array(7) 创建一个长度为 7 的空数组(注意:这是指一个有 7 个空位(empty)的数组,而不是由 7 个 undefined 组成的数组)
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
Array.of(undefined); // [undefined]
Array(7); // [ , , , , , , ]
Array(1, 2, 3); // [1, 2, 3]

Array.isArray()

用于确定传递的值是否是一个 Array。

// 下面的函数调用都返回 true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
Array.isArray(new Array('a', 'b', 'c', 'd'));
// 鲜为人知的事实:其实 Array.prototype 也是一个数组。
Array.isArray(Array.prototype);

// 下面的函数调用都返回 false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray(new Uint8Array(32));
Array.isArray({ __proto__: Array.prototype });
// 当检测Array实例时, Array.isArray 优于 instanceof,因为Array.isArray能检测iframes.

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length - 1].Array;
var arr = new xArray(1, 2, 3); // [1,2,3]

// Correctly checking for Array
Array.isArray(arr); // true
// Considered harmful, because doesn't work though iframes
arr instanceof Array; // false

map()

  • 创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
  • map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。 callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。
  • callback 函数会被自动传入三个参数:数组元素,元素索引,原数组本身。
  • 如果 thisArg 参数提供给 map,则会被用作回调函数的 this 值。否则 undefined 会被用作回调函数的 this 值。 this 的值最终相对于 callback 函数的可观察性是依据 function 决定的
  • map 不修改调用它的原数组本身(当然可以在 callback 执行时改变原数组)

示例

求数组中每个元素的平方根

var numbers = [1, 4, 9];
var roots = numbers.map(item => {
  return Math.sqrt(item);
});
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]

因为 Math.sqrt 只有一个参数,所以可简写为

var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]

需要注意 parseInt 方法是有两个参数的

['1', '2', '3'].map(parseInt); // 期望输出 [1, 2, 3], 而实际结果是 [1, NaN, NaN].

解决方法

function returnInt(element) {
  return parseInt(element, 10);
}

['1', '2', '3'].map(returnInt); // [1, 2, 3]

使用 map 重新格式化数组中的对象

var kvArray = [
  { key: 1, value: 10 },
  { key: 2, value: 20 },
  { key: 3, value: 30 },
];

var reformattedArray = kvArray.map(function (obj) {
  var rObj = {};
  rObj[obj.key] = obj.value;
  return rObj;
});

// reformattedArray 数组为: [{1: 10}, {2: 20}, {3: 30}],

// kvArray 数组未被修改:
// [{key: 1, value: 10},
//  {key: 2, value: 20},
//  {key: 3, value: 30}]

Mapping 含 undefined 的数组 当返回 undefined 或没有返回任何内容时:

var numbers = [1, 2, 3, 4];
var filteredNumbers = numbers.map(function (num, index) {
  if (index < 3) {
    return num;
  }
});
//index goes from 0,so the filterNumbers are 1,2,3 and undefined.
// filteredNumbers is [1, 2, 3, undefined]
// numbers is still [1, 2, 3, 4]

flatMap()

使用映射函数映射每个元素,然后将结果压缩成一个新数组。 callback 可以生成一个新数组中的元素的函数,可以传入三个参数:

  • currentValue:当前正在数组中处理的元素
  • index:可选的。数组中正在处理的当前元素的索引。
  • array:可选的。被调用的 map 数组
  • thisArg:可选的。执行 callback 函数时 使用的 this 值。
  • 返回值: 一个新的数组,其中每个元素都是回调函数的结果,并且结构深度 depth 值为 1。

map() 与 flatMap()

var arr1 = [1, 2, 3, 4];

// [[2], [4], [6], [8]]
arr1.map(x => [x * 2]);

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

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

下面我们将包含几句话的数组拆分成单个汉字组成的新数组。

let arr1 = ["it's Sunny in", '', 'California'];

// [["it's","Sunny","in"],[""],["California"]]
arr1.map(x => x.split(' '));

// ["it's","Sunny","in", "", "California"]
arr1.flatMap(x => x.split(' '));

注意,输出列表长度可以不同于输入列表长度。

在一个 map() 期间增加或去除一些项

  • flatMap 能用于在 map 期间增删项目(也就是修改 items 的数量)。
  • 换句话说,它允许你遍历很多项使之成为另一些项(靠分别把它们放进去来处理),而不是总是一对一。
  • 从这个意义上讲,它的作用类似于 filter 的对立面。只需返回一个 1 项元素数组以保留该项,返回一个多元素数组以添加项,或返回一个 0 项元素数组以删除该项。
let a = [5, 4, -3, 20, 17, -33, -4, 18];
// |\  \  x   |  | \   x   x   |
// [4,1, 4,   20, 16, 1,       18]
a.flatMap(n => (n < 0 ? [] : n % 2 == 0 ? [n] : [n - 1, 1]));

// expected output: [4, 1, 4, 20, 16, 1, 18]

find()

  • 返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
  • find 方法对数组中的每一项元素执行一次 callback 函数,直至有一个 callback 返回 true。当找到了这样一个元素后,该方法会立即返回这个元素的值,否则返回 undefined。
  • 注意 callback 函数会为数组中的每个索引调用即从 0 到 length - 1,而不仅仅是那些被赋值的索引,这意味着对于稀疏数组来说,该方法的效率要低于那些只遍历有值的索引的方法。
  • callback 函数带有 3 个参数:当前元素的值、当前元素的索引,以及数组本身。
  • 如果提供了 thisArg 参数,那么它将作为每次 callback 函数执行时的 this ,如果未提供,则使用 undefined。
  • find 方法不会改变数组。
var inventory = [
  { name: 'apples', quantity: 2 },
  { name: 'bananas', quantity: 0 },
  { name: 'cherries', quantity: 5 },
];

function findCherries(fruit) {
  return fruit.name === 'cherries';
}

console.log(inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }
var arr = [
  { name: 'fx', age: 18 },
  { name: 'wfd', age: 19 },
];
var arrTest = arr.find((item, index, array) => {
  console.log(
    '当前值:' + JSON.stringify(item),
    '当前值的索引' + index,
    '当前数组' + array,
  );
  return item.age >= 18;
});
console.log(arrTest);
var arr = [
  { name: 'fx', age: 18 },
  { name: 'wfd', age: 19 },
];
var arrTest = arr.find((item, index, array) => {
  console.log(
    '当前值:' + JSON.stringify(item),
    '当前值的索引' + index,
    '当前数组' + array,
  );
  return item.name === 'wfd';
});
console.log(arrTest);

下面的例子展示了如何从一个数组中寻找质数(如果找不到质数则返回 undefined)

function isPrime(element, index, array) {
  var start = 2;
  while (start <= Math.sqrt(element)) {
    if (element % start++ < 1) {
      return false;
    }
  }
  return element > 1;
}

console.log([4, 6, 8, 12].find(isPrime)); // undefined, not found
console.log([4, 5, 8, 12].find(isPrime)); // 5

当在回调中删除数组中的一个值时,当访问到这个位置时,其传入的值是 undefined:

findIndex()

  • 返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。
  • findIndex 方法对数组中的每个数组索引 0..length-1(包括)执行一次 callback 函数,直到找到一个 callback 函数返回真实值(强制为 true)的值。
  • 如果找到这样的元素,findIndex 会立即返回该元素的索引。
  • 如果回调从不返回真值,或者数组的 length 为 0,则 findIndex 返回 -1。
  • 与某些其他数组方法(如 Array#some)不同,在稀疏数组中,即使对于数组中不存在的条目的索引也会调用回调函数。
  • 回调函数调用时有三个参数:元素的值,元素的索引,以及被遍历的数组。
  • 如果一个 thisArg 参数被提供给 findIndex, 它将会被当作 this 使用在每次回调函数被调用的时候。如果没有被提供,将会使用 undefined。findIndex 不会修改所调用的数组。

以下示例查找数组中素数的元素的索引(如果不存在素数,则返回 -1)。

function isPrime(element, index, array) {
  var start = 2;
  while (start <= Math.sqrt(element)) {
    if (element % start++ < 1) {
      return false;
    }
  }
  return element > 1;
}

console.log([4, 6, 8, 12].findIndex(isPrime)); // -1, not found
console.log([4, 6, 7, 12].findIndex(isPrime)); // 2
var arr = [
  { name: 'fx', age: 18 },
  { name: 'wfd', age: 19 },
];
var arrTest = arr.findIndex((item, index, array) => {
  console.log(
    '当前值:' + JSON.stringify(item),
    '当前值的索引' + index,
    '当前数组' + array,
  );
  return item.name === 'wfd';
});
console.log(arrTest);

reduce()

对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。 reducer 函数接收 4 个参数:

  • Accumulator (acc) (累计器)
  • Current Value (cur) (当前值)
  • Current Index (idx) (当前索引)
  • Source Array (src) (源数组) 您的 reducer 函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。

回调函数第一次执行时,accumulator 和 currentValue 的取值有两种情况:

  • 如果调用 reduce() 时提供了 initialValue,accumulator 取值为 initialValue,currentValue 取数组中的第一个值;
  • 如果没有提供 initialValue,那么 accumulator 取数组中的第一个值,currentValue 取数组中的第二个值。
  • 如果没有提供 initialValue,reduce 会从索引 1 的地方开始执行 callback 方法,跳过第一个索引。如果提供 initialValue,从索引 0 开始。
  • 如果数组为空且没有提供 initialValue,会抛出 TypeError
  • 如果数组仅有一个元素(无论位置如何)并且没有提供 initialValue, 或者有提供 initialValue 但是数组为空,那么此唯一值将被返回并且 callback 不会被执行。
var maxCallback = (acc, cur) => Math.max(acc.x, cur.x);
var maxCallback2 = (max, cur) => Math.max(max, cur);
// reduce() 没有初始值
[{ x: 22 }, { x: 42 }].reduce(maxCallback); // 42
[{ x: 22 }].reduce(maxCallback); // { x: 22 }
[].reduce(maxCallback); // TypeError
// map/reduce; 这是更好的方案,即使传入空数组或更大数组也可正常执行
[{ x: 22 }, { x: 42 }].map(el => el.x).reduce(maxCallback2, -Infinity);

reduce() 如何运行 假如运行下段 reduce() 代码:

[0, 1, 2, 3, 4].reduce(
  function (accumulator, currentValue, currentIndex, array) {
    return accumulator + currentValue;
  },
);

// ===

[0, 1, 2, 3, 4].reduce((prev, curr) => prev + curr);

callback 被调用四次,每次调用的参数和返回值如下表:

callbackaccumulatorcurrentValuecurrentIndexarrayreturnvalue
first call011[0, 1, 2, 3, 4]1
second call122[0, 1, 2, 3, 4]3
third call333[0, 1, 2, 3, 4]6
fourth call644[0, 1, 2, 3, 4]10

提供一个初始值作为 reduce()方法的第二个参数,以下是运行过程及结果:

[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => {
  return accumulator + currentValue;
}, 10);
callbackaccumulatorcurrentValuecurrentIndexarrayreturnvalue
first call1000[0, 1, 2, 3, 4]1
second call1011[0, 1, 2, 3, 4]3
third call1122[0, 1, 2, 3, 4]6
fourth call1333[0, 1, 2, 3, 4]10
fifth call1644[0, 1, 2, 3, 4]10
💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEJavaScript 数组 Array 方法大全与用法详解加载中