ES6 基础知识之Array 系列三

238 阅读12分钟

这是Array系列三,我们接着往下看,如果有兴趣的朋友可以去看一下第一期和第二期。

1,Array.prototype.includes()

includes()方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。

// 声明
// searchElement 查找的元素
// fromIndex     从第几个开始
includes(searchElement: T, fromIndex?: number): boolean;


// 例子
const array1 = [1, 2, 3];

console.log(array1.includes(2));
// Expected output: true

const pets = ['cat', 'dog', 'bat'];

console.log(pets.includes('cat'));
// Expected output: true

console.log(pets.includes('at'));
// Expected output: false

2,Array.prototype.filter()

filter()方法创建给定数组一部分的浅拷贝,其包含通过所提供函数实现的测试的所有元素。

// 声明
// predicate 传入一个方法
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];


// 例子
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];

const result = words.filter(word => word.length > 6);

console.log(result);
// Expected output: Array ["exuberant", "destruction", "present"]

3,Array.prototype.sort()

sort() 方法就地对数组的元素进行排序,并返回对相同数组的引用。默认排序是将元素转换为字符串,然后按照它们的 UTF-16 码元值升序排序。

由于它取决于具体实现,因此无法保证排序的时间和空间复杂度。

如果想要不改变原数组的排序方法,可以使用 toSorted()

sort 可以传递一个方法 compareFn,返回值对应着排序的规则:

compareFn(a, b) 返回值排序顺序
> 0a 在 b 后,如 [b, a]
< 0a 在 b 前,如 [a, b]
=== 0保持 a 和 b 原来的顺序
// 声明
sort(compareFn?: (a: number, b: number) => number): this;

// 例子
const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// Expected output: Array ["Dec", "Feb", "Jan", "March"]

const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// Expected output: Array [1, 100000, 21, 30, 4]

// 升序
array1.sort((a, b) => a - b);
console.log(array1);
// Expected output: Array [1, 4, 21, 30, 100000]

// 降序
array1.sort((a, b) => b - a);
console.log(array1);
// Expected output: Array [100000, 30, 21, 4, 1]


const items = [
  { name: "Edward", value: 21 },
  { name: "Sharpe", value: 37 },
  { name: "And", value: 45 },
  { name: "The", value: -12 },
  { name: "Magnetic", value: 13 },
  { name: "Zeros", value: 37 },
];

// 根据 value 排序
items.sort((a, b) => a.value - b.value);
console.log(items);
// Expected output: Array [{"name":"The","value":-12},{"name":"Magnetic","value":13},
// {"name":"Edward","value":21},{"name":"Sharpe","value":37},{"name":"Zeros","value":37},
// {"name":"And","value":45}]

// 根据 name 排序
items.sort((a, b) => {
  const nameA = a.name.toUpperCase(); // 忽略大小写
  const nameB = b.name.toUpperCase(); // 忽略大小写
  if (nameA < nameB) {
    return -1;
  }
  if (nameA > nameB) {
    return 1;
  }

  // name 必须相等
  return 0;
});

// Expected output: Array [{"name":"And","value":45},{"name":"Edward","value":21},
// {"name":"Magnetic","value":13},{"name":"Sharpe","value":37},{"name":"The","value":-12},
// {"name":"Zeros","value":37}]

4,Array.prototype.toSorted()

Array实例的toSorted()方法是sort()方法的复制方法版本。它返回一个新数组,其元素按升序排列。

// 语法

// 不传入函数
toSorted()

// 传入箭头函数
toSorted((a, b) => { /* … */ })

// 传入比较函数
toSorted(compareFn)

// 內联比较函数
toSorted(function compareFn(a, b) { /* … */ })

// 例子
const months = ["Mar", "Jan", "Feb", "Dec"];
const sortedMonths = months.toSorted();
console.log(sortedMonths); // ['Dec', 'Feb', 'Jan', 'Mar']
console.log(months); // ['Mar', 'Jan', 'Feb', 'Dec']

const values = [1, 10, 21, 2];
const sortedValues = values.toSorted((a, b) => a - b));
console.log(sortedValues); // [1, 2, 10, 21]
console.log(values); // [1, 10, 21, 2]

5,Array.prototype.map()

map()方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。

// 声明
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];


// 例子
const array1 = [1, 4, 9, 16];

// Pass a function to map
const map1 = array1.map(x => x * 2);

console.log(map1);
// Expected output: Array [2, 8, 18, 32]


// 可以用于对象数组输出新的内容
const items = [
  { name: "Edward", value: 21 },
  { name: "Sharpe", value: 37 },
  { name: "And", value: 45 },
  { name: "The", value: -12 },
  { name: "Magnetic", value: 13 },
  { name: "Zeros", value: 37 },
];

console.log(items.map(v => v.name));
// Expected output: Array ['Edward', 'Sharpe', 'And', 'The', 'Magnetic', 'Zeros']

6,Array.prototype.forEach()

forEach()方法对数组的每个元素执行一次给定的函数。

// 声明
forEach(callbackfn: (value: number, index: number, array: Int8Array) => void, thisArg?: any): void;

// 例子
const array1 = ['a', 'b', 'c'];

array1.forEach(element => console.log(element));

// Expected output: "a"
// Expected output: "b"
// Expected output: "c"

7,Array.prototype.flat()

flat()方法创建一个新的数组,并根据指定深度递归地将所有子数组元素拼接到新的数组中。

// 语法 
// depth 指定要提取嵌套数组的结构深度,默认值为 1。
flat()
flat(depth)


// 例子
const arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

const arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

8,Array.prototype.flatMap()

flatMap()方法对数组中的每个元素应用给定的回调函数,然后将结果展开一级,返回一个新数组。它等价于在调用map()方法后再调用深度为 1 的flat()方法(arr.map(...args).flat()),但比分别调用这两个方法稍微更高效一些。

// 语法
// callbackFn 一个在数组的每个元素上执行的函数。它应该返回一个包含新数组元素的数组,或是要添加到新数组中的单个非数组值。该函数将被传入以下参数:
// element 数组中正在处理的当前元素。
// index   数组中正在处理的当前元素的索引。
// array   调用 flatMap() 的当前数组。
// thisArg 在执行 callbackFn 时用作 this 的值
// 返回值   一个新的数组,其中每个元素都是回调函数的结果,并且被展开一级。
flatMap(callbackFn)
flatMap(callbackFn, thisArg)

// 例子
const arr1 = [1, 2, 3, 4];

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

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

// 只有一层被展平
arr1.flatMap((x) => [[x * 2]]);
// [[2], [4], [6], [8]]

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

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

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

const items = [  { name: 'Edward', value: 21, items: [1, 2, 3] },
  { name: 'Sharpe', value: 37, items: [4, 5, 6] },
  { name: 'And', value: 45, items: [7, 8, 9] },
  { name: 'The', value: -12, items: [10, 12, 13] },
  { name: 'Magnetic', value: 13, items: [21, 22, 23] },
  { name: 'Zeros', value: 37, items: [31, 32, 33] }
];

console.log(items.flatMap((v) => v.items));
//  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 21, 22, 23, 31, 32, 33]

9,Array.prototype.reduce()

reduce() 方法对数组中的每个元素按序执行一个提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。

第一次执行回调函数时,不存在“上一次的计算结果”。如果需要回调函数从数组索引为 0 的元素开始执行,则需要传递初始值。否则,数组索引为 0 的元素将被用作初始值,迭代器将从第二个元素开始执行(即从索引为 1 而不是 0 的位置开始)。

下面的例子能够帮助你理解 reduce() 的用处——计算数组所有元素的总和:

// 语法
// previousValue  上一次调用 callbackFn 的结果。在第一次调用时,如果指定了 initialValue 则为指定的值,
//                否则为 array[0] 的值。
// currentValue   当前元素的值。在第一次调用时,如果指定了 initialValue,则为 array[0] 的值,否则为 array[1]。
// currentIndex   currentValue 在数组中的索引位置。在第一次调用时,如果指定了 initialValue 则为 0,否则为 1。 
// array          调用了 reduce() 的数组本身。 
// initialValue   第一次调用回调时初始化 accumulator 的值。如果指定了 initialValue,
//                则 callbackFn 从数组中的第一个值作为 currentValue 开始执行。
//                如果没有指定 initialValue,则 accumulator 初始化为数组中的第一个值,
//                并且 callbackFn 从数组中的第二个值作为 currentValue 开始执行。
//                在这种情况下,如果数组为空(没有第一个值可以作为 accumulator 返回),则会抛出错误。 
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;

// 例子
const objects = [{ x: 1 }, { x: 2 }, { x: 3 }];
const sum = objects.reduce(
  (accumulator, currentValue) => accumulator + currentValue.x,
  0,
);

console.log(sum); // 6

// friends——一个对象数组,其中对象字段“books”是最喜欢的书的列表
const friends = [
  {
    name: "Anna",
    books: ["Bible", "Harry Potter"],
    age: 21,
  },
  {
    name: "Bob",
    books: ["War and peace", "Romeo and Juliet"],
    age: 26,
  },
  {
    name: "Alice",
    books: ["The Lord of the Rings", "The Shining"],
    age: 18,
  },
];

// allbooks——列表,其中包含所有朋友的书籍和 initialValue 中包含的附加列表
const allbooks = friends.reduce(
  (accumulator, currentValue) => [...accumulator, ...currentValue.books],
  ["Alphabet"],
);
console.log(allbooks);
// [
//   'Alphabet', 'Bible', 'Harry Potter', 'War and peace',
//   'Romeo and Juliet', 'The Lord of the Rings',
//   'The Shining'
// ]

// 去重
const myArray = ["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"];
const myArrayWithNoDuplicates = myArray.reduce((accumulator, currentValue) => {
  if (!accumulator.includes(currentValue)) {
    return [...accumulator, currentValue];
  }
  return accumulator;
}, []);

console.log(myArrayWithNoDuplicates);

10,Array.prototype.reduceRight()

reduceRight() 方法对累加器(accumulator)和数组的每个值(按从右到左的顺序)应用一个函数,并使其成为单个值。

对于从左至右遍历的相似方法,请参阅 Array.prototype.reduce()

// 语法
// 参数同 reduce
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;


// 例子, reduce 与 reduceRight 之间的区别
const a = ["1", "2", "3", "4", "5"];
const left = a.reduce((prev, cur) => prev + cur);
const right = a.reduceRight((prev, cur) => prev + cur);

console.log(left); // "12345"
console.log(right); // "54321"

11,Array.prototype.reverse()

reverse() 方法就地反转数组中的元素,并返回同一数组的引用。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。换句话说,数组中的元素顺序将被翻转,变为与之前相反的方向。

要在不改变原始数组的情况下反转数组中的元素,使用 toReversed()

const items = [1, 2, 3];
console.log(items); // [1, 2, 3]

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


const numbers = [3, 2, 4, 1, 5];
// [...numbers] 创建一个浅拷贝,因此 reverse() 不会改变原始数据
const reverted = [...numbers].reverse();
reverted[0] = 5;
console.log(numbers[0]); // 3

12,Array.prototype.toReversed()

Array实例的toReversed()方法是reverse()方法对应的复制版本。它返回一个元素顺序相反的新数组。

const items = [1, 2, 3];
console.log(items); // [1, 2, 3]

const reversedItems = items.toReversed();
console.log(reversedItems); // [3, 2, 1]
console.log(items); // [1, 2, 3]

13,Array.prototype.unshift()

unshift()方法将指定元素添加到数组的开头,并返回数组的新长度。

let arr = [4, 5, 6];

arr.unshift(1, 2, 3);
console.log(arr);
// [1, 2, 3, 4, 5, 6]

arr = [4, 5, 6]; // 重置数组

arr.unshift(1);
arr.unshift(2);
arr.unshift(3);

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

const arr = [1, 2];

arr.unshift(0); // 调用的结果是 3,这是新的数组长度。
// 数组是 [0, 1, 2]

arr.unshift(-2, -1); // 新的数组长度是 5
// 数组是 [-2, -1, 0, 1, 2]

arr.unshift([-4, -3]); // 新的数组长度是 6
// 数组是 [[-4, -3], -2, -1, 0, 1, 2]

arr.unshift([-7, -6], [-5]); // 新的数组长度是 8
// 数组是 [ [-7, -6], [-5], [-4, -3], -2, -1, 0, 1, 2 ]

14,Array.prototype.shift()

shift()方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。

const myFish = ["angel", "clown", "mandarin", "surgeon"];

console.log("调用 shift 之前:", myFish);
// 调用 shift 之前: ['angel', 'clown', 'mandarin', 'surgeon']

const shifted = myFish.shift();

console.log('调用 shift 之后:', myFish);
// 调用 shift 之后: ['clown', 'mandarin', 'surgeon']

console.log('被删除的元素:' + shifted);
// "被删除的元素:angel"


const names = ["Andrew", "Tyrone", "Paul", "Maria", "Gayatri"];

while (typeof (i = names.shift()) !== "undefined") {
  console.log(i);
}
// Andrew, Tyrone, Paul, Maria, Gayatri

15,Array.prototype.slice()

slice()方法返回一个新的数组对象,这一对象是一个由start和end决定的原数组的浅拷贝(包括start,不包括end),其中start和end代表了数组元素的索引。原始数组不会被改变。

// 语法
// start 提取起始处的索引(从 0 开始),会转换为整数。
//       如果索引是负数,则从数组末尾开始计算——如果 start < 0,则使用 start + array.length。
//       如果 start < -array.length 或者省略了 start,则使用 0//       如果 start >= array.length,则不提取任何元素。
// end   提取终止处的索引(从 0 开始),会转换为整数。slice() 会提取到但不包括 end 的位置。
//       如果索引是负数,则从数组末尾开始计算——如果 end < 0,则使用 end + array.length。
//       如果 end < -array.length,则使用 0//       如果 end >= array.length 或者省略了 end,则使用 array.length,提取所有元素直到末尾。
//       如果 end 在规范化后小于或等于 start,则不提取任何元素。
slice()
slice(start)
slice(start, end)


// 例子
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

console.log(animals.slice(2));
// Expected output: Array ["camel", "duck", "elephant"]

console.log(animals.slice(2, 4));
// Expected output: Array ["camel", "duck"]

console.log(animals.slice(1, 5));
// Expected output: Array ["bison", "camel", "duck", "elephant"]

console.log(animals.slice(-2));
// Expected output: Array ["duck", "elephant"]

console.log(animals.slice(2, -1));
// Expected output: Array ["camel", "duck"]

console.log(animals.slice());
// Expected output: Array ["ant", "bison", "camel", "duck", "elephant"]

16,Array.prototype.splice()

splice() 方法通过移除或者替换已存在的元素和/或添加新元素就地改变一个数组的内容。

要创建一个删除和/或替换部分内容而不改变原数组的新数组,请使用 toSpliced()。要访问数组的一部分而不修改它,参见 slice()

// 语法
// start  从 0 开始计算的索引,表示要开始改变数组的位置,它会被转换成整数。
//        负索引从数组末尾开始计算——如果 start < 0,使用 start + array.length。
//        如果 start < -array.length,使用 0。
//        如果 start >= array.length,则不会删除任何元素,但是该方法会表现为添加元素的函数,添加所提供的那些元素。
//        如果 start 被省略了(即调用 splice() 时不传递参数),则不会删除任何元素。这与传递 undefined 不同,后者会被转换为 0。
// deleteCount 一个整数,表示数组中要从 start 开始删除的元素数量。
//        如果省略了 deleteCount,或者其值大于或等于由 start 指定的位置到数组末尾的元素数量,
//        那么从 start 到数组末尾的所有元素将被删除。但是,如果你想要传递任何 itemN 参数,
//        则应向 deleteCount 传递 Infinity 值,以删除 start 之后的所有元素,因为显式的 undefined 会转换为 0。
//        如果 deleteCount 是 0 或者负数,则不会移除任何元素。在这种情况下,你应该至少指定一个新元素
// item1, …, itemN 从 start 开始要加入到数组中的元素。如果不指定任何元素,splice() 将只从数组中删除元素。
splice(start)
splice(start, deleteCount)
splice(start, deleteCount, item1)
splice(start, deleteCount, item1, item2, itemN)

// 例子
// 移除索引 2 之前的 0(零)个元素,并插入“drum”
const myFish = ["angel", "clown", "mandarin", "sturgeon"];
const removed = myFish.splice(2, 0, "drum");

// 运算后的 myFish 是 ["angel", "clown", "drum", "mandarin", "sturgeon"]
// removed 是 [],没有元素被删除

// 移除索引 2 之前的 0(零)个元素,并插入“drum”和“guitar”
const myFish = ["angel", "clown", "mandarin", "sturgeon"];
const removed = myFish.splice(2, 0, "drum", "guitar");

// 运算后的 myFish 是 ["angel", "clown", "drum", "guitar", "mandarin", "sturgeon"]
// removed 是 [],没有元素被删除

// 在索引 3 处移除 1 个元素
const myFish = ["angel", "clown", "drum", "mandarin", "sturgeon"];
const removed = myFish.splice(3, 1);

// 运算后的 myFish 是 ["angel", "clown", "drum", "sturgeon"]
// removed 是 ["mandarin"]

// 从索引 -2 处移除 1 个元素
const myFish = ["angel", "clown", "mandarin", "sturgeon"];
const removed = myFish.splice(-2, 1);

// 运算后的 myFish 是 ["angel", "clown", "sturgeon"]
// removed 是 ["mandarin"]

17,Array.prototype.toSpliced()

Array实例的toSpliced()方法是splice()方法的复制版本。它返回一个新数组,并在给定的索引处删除和/或替换了一些元素。

// 语法
// 参数同splice
toSpliced(start)
toSpliced(start, deleteCount)
toSpliced(start, deleteCount, item1)
toSpliced(start, deleteCount, item1, item2, itemN)

// 例子
const months = ["Jan", "Mar", "Apr", "May"];

// 在索引 1 处添加一个元素
const months2 = months.toSpliced(1, 0, "Feb");
console.log(months2); // ["Jan", "Feb", "Mar", "Apr", "May"]

// 从第 2 个索引开始删除两个元素
const months3 = months2.toSpliced(2, 2);
console.log(months3); // ["Jan", "Feb", "May"]

// 在索引 1 处用两个新元素替换一个元素
const months4 = months3.toSpliced(1, 1, "Feb", "Mar");
console.log(months4); // ["Jan", "Feb", "Mar", "May"]

// 原数组不会被修改
console.log(months); // ["Jan", "Mar", "Apr", "May"]