5. JS Array专项复习2 ——数组的实例方法

JS Array专项复习2 ——数组的实例方法

其实资深开发者都懂,不懂也知道查,或者GPT,但是有的时候面试官可能会问数组的实例方法这种问题,另外将数组实例方法巧记于心在开发中可以省去查找咨询GPT的时间**(毕竟当你懂了之后,问GPT的效率也会变高)**。🏳️‍🌈标识的是我工作中经常用到的

3. 数组实例方法

3.1 Array.prototype.at()🏳️

at 在这里翻译过来就是在(位置) 像小学英语中的at home(在家)一样 ,本质上就是找数组在索引位置上的元素,只不过你不仅可以正着找也可以倒着找

3.1.1 语法

at() 方法接收一个整数值并返回该索引对应的元素,允许正数和负数。负整数从数组中的最后一个元素开始倒数。

at(index)

index

要返回的数组元素的索引(从零开始),会被转换为整数。负数索引从数组末尾开始计数——如果 index < 0,则会访问 index + array.length 位置的元素。。

返回值

返回数组中与给定索引匹配的元素。如果 index < -array.lengthindex >= array.length,则总是返回 undefined,而不会尝试访问相应的属性。

3.1.2 示例
1. 返回数组中的最后一个值
// 数组及数组元素
const cart = ["apple", "banana", "pear"];

// 一个函数,用于返回给定数组的最后一个元素
function returnLast(arr) {
  return arr.at(-1);
}

// 获取 'cart' 数组的最后一个元素
const item1 = returnLast(cart);
console.log(item1); // 输出:'pear'

// 在 'cart' 数组中添加一个元素
cart.push("orange");
const item2 = returnLast(cart);
console.log(item2); // 输出:'orange'
Array.from("foo");
// [ "f", "o", "o" ]
2. 在非数组对象中调用at()

at() 方法读取 thislength 属性并计算需要访问的索引。 也就是在类数组对象中使用也是可以的。

const arrayLike = {
  length: 2,
  0: "a",
  1: "b",
};
console.log(Array.prototype.at.call(arrayLike, -1)); // "b"

3.2 Array.prototype.concat()🏳️

concat 就是concatenate的缩写,翻译过来就是连接,这个函数在很多语言中都有,记不住这个单词可以去看里 concatenate | “concatenate” 的词源、“concatenate” 的起源和意思 - etymonline

3.2.1 语法

concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

concat()
concat(value0)
concat(value0, value1)
concat(value0, value1, /* … ,*/ valueN)

valueN

valueN 是数组或者是一个值,valueN 将被合并到一个新的数组中。如果省略了所有 valueN 参数,则 concat 会返回现存数组的一个浅拷贝。

返回值

一个新的数组

3.2.2 示例
1. 连接两个数组
const letters = ["a", "b", "c"];
const numbers = [1, 2, 3];

const alphaNumeric = letters.concat(numbers);
console.log(alphaNumeric);
// results in ['a', 'b', 'c', 1, 2, 3]

2. 连接三个数组

at() 方法读取 thislength 属性并计算需要访问的索引。

const num1 = [1, 2, 3];
const num2 = [4, 5, 6];
const num3 = [7, 8, 9];
const numbers = num1.concat(num2, num3);
console.log(numbers);
// results in [1, 2, 3, 4, 5, 6, 7, 8, 9]
3. 将值合并到数组
const letters = ["a", "b", "c"];
const alphaNumeric = letters.concat(1, [2, 3]);
console.log(alphaNumeric);
// results in ['a', 'b', 'c', 1, 2, 3]
4. 合并嵌套数组
const num1 = [[1]];
const num2 = [2, [3]];
const numbers = num1.concat(num2);
console.log(numbers);
// [[1], 2, [3]]
// 修改 num1 的第一个元素
num1[0].push(4);
console.log(numbers);
// [[1, 4], 2, [3]]
5. 类数组对象中使用concat()

concat 默认情况下不会将类数组对象视作数组——仅在 Symbol.isConcatSpreadable 被设置为真值(例如,true)时才会将类数组对象视作数组。

const obj1 = { 0: 1, 1: 2, 2: 3, length: 3 };
const obj2 = { 0: 1, 1: 2, 2: 3, length: 3, [Symbol.isConcatSpreadable]: true };//加个这玩意
console.log([0].concat(obj1, obj2));
// [ 0, { '0': 1, '1': 2, '2': 3, length: 3 }, 1, 2, 3 ]

3.3 Array.prototype.copyWithin()🏳️

copy: 复制 within 在…之内 翻译过来就是在内部复制

3.3.1 语法

copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。

copyWithin(target)
copyWithin(target, start)
copyWithin(target, start, end) //三个参数均为数字

target

将要被替换/覆盖的索引

  • 负索引将从数组末尾开始计数——如果 target < 0,则实际是 target + array.length
  • 如果 target < -array.length,则使用 0
  • 如果 target >= array.length,则不会拷贝任何内容。
  • 如果 target 位于 start 之后,则复制只会持续到 array.length 结束(换句话说,copyWithin() 永远不会扩展数组)。

start 默认为0

内部复制的起始索引

  • 负索引将从数组末尾开始计数——如果 start < 0,则实际是 start + array.length
  • 如果省略 startstart < -array.length,则默认为 0
  • 如果 start >= array.length,则不会拷贝任何内容。

end 默认为数组长度arr.length

内部复制的结束索引

copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。

  • 负索引将从数组末尾开始计数——如果 end < 0,则实际是 end + array.length
  • 如果 end < -array.length,则使用0
  • 如果省略 endend >= array.length,则默认为 array.length,这将导致直到数组末尾的所有元素都被复制。
  • 如果 end 位于 start 之前,则不会拷贝任何内容。

返回值

改变后的数组。

3.3.2 示例
1. 使用copywithin
console.log([1, 2, 3, 4, 5].copyWithin(-2));
// [1, 2, 3, 1, 2]

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

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

console.log([1, 2, 3, 4, 5].copyWithin(-2, -3, -1));
// [1, 2, 3, 3, 4]
2. 在稀疏数组上使用

copywithin将会保留空槽

console.log([1, , 3].copyWithin(2, 1, 2)); // [1, empty, empty]
3. 在非数组对象中使用copywithin

copyWithin() 方法读取 thislength 属性,然后操作所涉及的整数索引。

const arrayLike = {
  length: 5,
  3: 1,
};
console.log(Array.prototype.copyWithin.call(arrayLike, 0, 3));
// { '0': 1, '3': 1, length: 5 }
console.log(Array.prototype.copyWithin.call(arrayLike, 3, 1));
// { '0': 1, length: 5 }
// '3' 属性被删除,因为在复制的源中是一个空槽

3.4 Array.prototype.entries()🏳️

entries: 这个词的意思是“条目”或“项”。可以联想成“每个条目都有一个编号(索引)和内容(值)”。

3.4.1 语法

Array.prototype.entries() 是 JavaScript 中数组的一个方法,用于返回一个新的数组迭代器对象,该对象包含数组中每个元素的键值对(索引和值)

entries()

返回值

一个新的可迭代对象。

3.4.2 示例
1. 迭代元素索引和元素值
const arr = ['apple', 'banana', 'cherry'];
const iterator = arr.entries();

for (const [index, value] of iterator) {
    console.log(index, value);
}
// 0 apple
// 1 banana
// 2 cherry
2. 使用for…of循环
const array = ["a", "b", "c"];
const arrayEntries = array.entries();

for (const element of arrayEntries) {
  console.log(element);
}

// [0, 'a']
// [1, 'b']
// [2, 'c']
3. 迭代稀疏数组

entries() 将访问空槽,就像它们是 undefined 一样。

for (const element of [, "a"].entries()) {
  console.log(element);
}
// [0, undefined]
// [1, 'a']
4. 在非数组对象中调用entries()

entries() 方法读取 thislength 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: "a",
  1: "b",
  2: "c",
};
for (const entry of Array.prototype.entries.call(arrayLike)) {
  console.log(entry);
}
// [ 0, 'a' ]
// [ 1, 'b' ]
// [ 2, 'c' ]

3.5 Array.prototype.every()🏳️

every 所有 所有都符合要求

它还有一个好兄弟 some 后面就不介绍了 语法解构和它一样 只不过是只要有一个符合要求就行

3.5.1 语法

every() 方法测试一个数组内的所有元素是否都能通过指定函数的测试。它返回一个布尔值。如果在数组中每个元素使得提供的函数返回 true,则返回 true;否则返回 false。

some() 方法测试数组中是否至少有一个元素通过了由提供的函数实现的测试。如果在数组中找到一个元素使得提供的函数返回 true,则返回 true;否则返回 false。

every(callbackFn)
every(callbackFn, thisArg)

callbackFn

为数组中的每个元素执行的函数。它应该返回一个真值以指示元素通过测试,否则返回一个假值。该函数被调用时将传入以下参数:

  • element

    数组中当前正在处理的元素。

  • index

    正在处理的元素在数组中的索引。

  • array

    调用了 every() 的数组本身。

thisArg

执行 callbackFn 时用作 this 的值。参见迭代方法

返回值

如果 callbackFn 为每个数组元素返回真值,则为 true。否则为 false

3.5.2 示例
1. 检查所有数组元素的大小
function isBigEnough(element, index, array) {
  return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough); // false
[12, 54, 18, 130, 44].every(isBigEnough); // true

2. 检查一个数组是否是另一个数组的子集
const isSubset = (array1, array2) =>
  array2.every((element) => array1.includes(element));

console.log(isSubset([1, 2, 3, 4, 5, 6, 7], [5, 7, 6])); // true
console.log(isSubset([1, 2, 3, 4, 5, 6, 7], [5, 8, 7])); // false
3. 在稀疏数组上使用every

every() 不会在空槽上运行它的断言函数。可以理解为跳过了

console.log([1, , 3].every((x) => x !== undefined)); // true
console.log([2, , 2].every((x) => x === 2)); // true
4. 在非数组对象上调用 every()
const arrayLike = {
  length: 3,
  0: "a",
  1: "b",
  2: "c",
};
console.log(
  Array.prototype.every.call(arrayLike, (x) => typeof x === "string"),
); // true

3.6 Array.prototype.fill()🏳️

fill 填充 可以简单理解为给一个数值,我将start位置到end位置的所有元素都填充为value

3.6.1 语法

fill() 方法用一个固定值填充一个数组中从起始索引(默认为 0)到终止索引(默认为 array.length)内的全部元素。它返回修改后的数组

fill(value)
fill(value, start)
fill(value, start, end)

value

用来填充数组元素的值。注意所有数组中的元素都将是这个确定的值:如果 value 是个对象,那么数组的每一项都会引用这个元素。

start

起始索引位置 默认为0

  • 负数索引从数组的末端开始计算,如果 start < 0,则使用 start + array.length
  • 如果 start < -array.lengthstart 被省略,则使用 0
  • 如果 start >= array.length,没有索引被填充。

end

结束索引位置 默认为array.length

  • 负数索引从数组的末端开始计算,如果 end < 0,则使用 end + array.length
  • 如果 end < -array.length,则使用 0
  • 如果 end >= array.lengthend 被省略,则使用 array.length,导致所有索引都被填充。
  • 如果经标准化后,end 的位置在 start 之前或之上,没有索引被填充。

返回值

value 填充修改后的数组。

3.6.2 示例
1. 使用fill
console.log([1, 2, 3].fill(4)); // [4, 4, 4]
console.log([1, 2, 3].fill(4, 1)); // [1, 4, 4]
console.log([1, 2, 3].fill(4, 1, 2)); // [1, 4, 3]
console.log([1, 2, 3].fill(4, 1, 1)); // [1, 2, 3]
console.log([1, 2, 3].fill(4, 3, 3)); // [1, 2, 3]
console.log([1, 2, 3].fill(4, -3, -2)); // [4, 2, 3]
console.log([1, 2, 3].fill(4, NaN, NaN)); // [1, 2, 3]
console.log([1, 2, 3].fill(4, 3, 5)); // [1, 2, 3]
console.log(Array(3).fill(4)); // [4, 4, 4]

// 一个简单的对象,被数组的每个空槽所引用
const arr = Array(3).fill({}); // [{}, {}, {}]
arr[0].hi = "hi"; // [{ hi: "hi" }, { hi: "hi" }, { hi: "hi" }]
2. 使用fill创建全1矩阵
const arr = new Array(3);
for (let i = 0; i < arr.length; i++) {
  arr[i] = new Array(4).fill(1); // 创建一个大小为 4 的数组,填充全 1
}
arr[0][0] = 10;
console.log(arr[0][0]); // 10
console.log(arr[1][0]); // 1
console.log(arr[2][0]); // 1
3. 使用fill填充空数组

此示例演示了如何填充数组,将所有值设定为一个特定的值。无需指定 end 参数。

const tempGirls = Array(5).fill("girl", 0);

注意,数组最初为稀疏数组,没有分配索引。fill() 仍然可以填充这个数组。

4. 非数组对象上使用fill()

fill() 方法读取 thislength 属性,并设置从 startend 的每个整数属性的值。

const arrayLike = { length: 2 };
console.log(Array.prototype.fill.call(arrayLike, 1));
// { '0': 1, '1': 1, length: 2 }

3.7 Array.prototype.filter()🏳️‍🌈

filter 过滤 过滤符合条件的元素 将这些元素组成一个新的数组

3.7.1 语法

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

filter(callbackFn)
filter(callbackFn, thisArg)

callbackFn

为数组中的每个元素执行的函数。它应该返回一个真值以将元素保留在结果数组中,否则返回一个假值。该函数被调用时将传入以下参数

element

数组中当前正在处理的元素。

index

正在处理的元素在数组中的索引。

array

调用了 filter() 的数组本身。

thisArg

执行 callbackFn 时用作 this 的值。

返回值

返回给定数组的一部分的浅拷贝,只包含能够通过callbackFn数组的值,如果所有元素都不能通过 那么就返回空数组

3.7.2 示例
1. 筛选大于10的元素
function isBigEnough(value) {
  return value >= 10;
}

const filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
// filtered is [12, 130, 44]
2. 找出数组中所有的素数
const array = [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];

function isPrime(num) {
  for (let i = 2; num > i; i++) {
    if (num % i === 0) {
      return false;
    }
  }
  return num > 1;
}

console.log(array.filter(isPrime)); // [2, 3, 5, 7, 11, 13]
3. 过滤json中的无效条目
const arr = [
  { id: 15 },
  { id: -1 },
  { id: 0 },
  { id: 3 },
  { id: 12.2 },
  {},
  { id: null },
  { id: NaN },
  { id: "undefined" },
];

let invalidEntries = 0;

function filterByID(item) {
  if (Number.isFinite(item.id) && item.id !== 0) {
    return true;
  }
  invalidEntries++;
  return false;
}

const arrByID = arr.filter(filterByID);

console.log("过滤后的数组\n", arrByID);
// 过滤后的数组
// [{ id: 15 }, { id: -1 }, { id: 3 }, { id: 12.2 }]

console.log("无效条目数量 =", invalidEntries);
// 无效条目数量 = 5
4. 在稀疏数组上使用 filter()

filter() 将跳过空槽。

console.log([1, , undefined].filter((x) => x === undefined)); // [undefined]
console.log([1, , undefined].filter((x) => x !== 2)); // [1, undefined]
5. 在非数组对象上使用filter()
const arrayLike = {
  length: 3,
  0: "a",
  1: "b",
  2: "c",
};
console.log(Array.prototype.filter.call(arrayLike, (x) => x <= "b"));
// [ 'a', 'b' ]

3.8 Array.prototype.find()🏳️‍🌈

find 寻找 目的就是找出元素

3.8.1 语法

find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回undefined。它还有一个亲兄弟,Array.prototype.findLast()语法与它一模一样,只不过是返回满足测试函数的最后一个元素的值

find(callbackFn)
find(callbackFn, thisArg)

callbackFn

为数组中的每个元素执行的函数。它应该返回一个真值来表示已经找到了匹配的元素。该函数被调用时将传入以下参数:

  • element

    数组中当前正在处理的元素。

  • index

    正在处理的元素在数组中的索引。

  • array

    调用了 find() 的数组本身。

thisArg

执行 callbackFn 时用作 this 的值

返回值

数组中第一个满足所提供测试函数的元素的值,否则返回undefined

3.8.2 示例
1. 在数组对象中通过属性进行查找
const inventory = [
  { name: "apples", quantity: 2 },
  { name: "bananas", quantity: 0 },
  { name: "cherries", quantity: 5 },
];

function isCherries(fruit) {
  return fruit.name === "cherries";
}

console.log(inventory.find(isCherries));
// { name: 'cherries', quantity: 5 }
2. 使用箭头函数和解构赋值
const inventory = [
  { name: "apples", quantity: 2 },
  { name: "bananas", quantity: 0 },
  { name: "cherries", quantity: 5 },
];

const result = inventory.find(({ name }) => name === "cherries");

console.log(result); // { name: 'cherries', quantity: 5 }
3. 在稀疏数组上使用find()

在稀疏数组中,空槽会被访问的,并被视为 undefined

// 声明一个在索引 2、3 和 4 处没有元素的数组
const array = [0, 1, , , , 5, 6];

// 将会打印所有索引,而不仅仅是那些有值的非空槽
array.find((value, index) => {
  console.log(`访问索引 ${index},值为 ${value}`);
});
// 访问索引 0,值为 0
// 访问索引 1,值为 1
// 访问索引 2,值为 undefined
// 访问索引 3,值为 undefined
// 访问索引 4,值为 undefined
// 访问索引 5,值为 5
// 访问索引 6,值为 6

// 打印所有索引,包括已删除的
array.find((value, index) => {
  // 在第一次迭代时删除元素 5
  if (index === 0) {
    console.log(`删除 array[5] 的值 ${array[5]}`);
    delete array[5];
  }
  // 即使删除了,元素 5 仍然被访问
  console.log(`访问索引 ${index},值为 ${value}`);
});
// 删除值为 5 的 array[5]
// 访问索引 0,值为 0
// 访问索引 1,值为 1
// 访问索引 2,值为 undefined
// 访问索引 3,值为 undefined
// 访问索引 4,值为 undefined
// 访问索引 5,值为 undefined
// 访问索引 6,值为 6
4. 在非数组对象上使用find()

find() 方法读取 thislength 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 7.3,
  2: 4,
};
console.log(Array.prototype.find.call(arrayLike, (x) => !Number.isInteger(x)));
// 7.3

3.9 Array.prototype.findIndex()🏳️‍🌈

显而易见,find寻找,index索引 findIndex就是寻找索引

3.9.1 语法

findIndex() 方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回 -1。它还有一个亲兄弟,Array.prototype.findLastIndex()语法与它一模一样,只不过是返回满足测试函数的最后一个索引

findIndex(callbackFn)
findIndex(callbackFn, thisArg)

callbackFn

为数组中的每个元素执行的函数。它应该返回一个真值来表示已经找到了匹配的元素。该函数被调用时将传入以下参数:

  • element

    数组中当前正在处理的元素。

  • index

    正在处理的元素在数组中的索引。

  • array

    调用了 findIndex() 的数组本身。

thisArg

执行 callbackFn 时用作 this 的值

返回值

数组中第一个满足所提供测试函数的元素的索引,否则返回-1

3.9.2 示例
1. 在数组中寻找首个素数的索引
function isPrime(element) {
  if (element % 2 === 0 || element < 2) {
    return false;
  }
  for (let factor = 3; factor <= Math.sqrt(element); factor += 2) {
    if (element % factor === 0) {
      return false;
    }
  }
  return true;
}

console.log([4, 6, 8, 9, 12].findIndex(isPrime)); // -1,没有找到
console.log([4, 6, 7, 9, 12].findIndex(isPrime)); // 2(array[2] 是 7)
2. 在稀疏数组中使用findIndex()

你可以搜索稀疏数组中的 undefined 并来获取空槽的索引。

console.log([1, , 3].findIndex((x) => x === undefined)); // 1
3. 在非数组对象中使用findIndex()
const arrayLike = {
  length: 3,
  0: 2,
  1: 7.3,
  2: 4,
};
console.log(
  Array.prototype.findIndex.call(arrayLike, (x) => !Number.isInteger(x)),
); // 1

3.10 Array.prototype.flat()🏳️

flat 平的 就像一个擀面杖把一个有很多层的数组给它碾平,可以传入参数深度来控制擀面杖的力度。

3.10.1 语法

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

flat()
flat(depth)
  • depth

    指定要提取嵌套数组的结构深度,默认值为 1。

返回值

一个新的数组,其中包含拼接后的子数组元素

3.10.2 示例
1. 展平嵌套数组
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]

2. 在稀疏数组上使用flat

flat() 方法删除数组中的空槽:(也就是说使用flat后最外层的数组不可能存在空槽,就像擀面杖擀的最外层表皮不可能有个大洞)

const arr5 = [1, 2, , 4, 5];
console.log(arr5.flat()); // [1, 2, 4, 5]

const array = [1, , 3, ["a", , "c"]];
console.log(array.flat()); // [ 1, 3, "a", "c" ]

const array2 = [1, , 3, ["a", , ["d", , "e"]]];
console.log(array2.flat()); // [ 1, 3, "a", ["d", empty, "e"] ] 
console.log(array2.flat(2)); // [ 1, 3, "a", "d", "e"]

3. 非数组对象使用flat()

flat() 方法读取 thislength 属性,然后访问每个整数索引。如果元素不是数组,则直接将其附加到结果中。如果元素是数组,则根据 depth 参数进行展开操作。

const arrayLike = {
  length: 3,
  0: [1, 2],
  // 嵌套的类数组对象不会被展平
  1: { length: 2, 0: 3, 1: 4 },
  2: 5,
};
console.log(Array.prototype.flat.call(arrayLike));
// [ 1, 2, { '0': 3, '1': 4, length: 2 }, 5 ]

3.11 Array.prototype.flatMap()🏳️

类似于先试用map再使用深度为1的flat

3.11.1 语法

flatMap() 方法对数组中的每个元素应用给定的回调函数,然后将结果展开一级,返回一个新数组。

它等价于在调用 map() 方法后再调用深度为 1 的 flat() 方法(arr.map(...args).flat()),但比分别调用这两个方法稍微更高效一些。

flatMap(callbackFn)
flatMap(callbackFn, thisArg)

callbackFn

一个在数组的每个元素上执行的函数。它应该返回一个包含新数组元素的数组,或是要添加到新数组中的单个非数组值。该函数将被传入以下参数:

  • element

    数组中正在处理的当前元素。

  • index

    数组中正在处理的当前元素的索引。

  • array

    调用 flatMap() 的当前数组。

    thisArg

    在执行 callbackFn 时用作 this 的值

返回值

一个新的数组,其中每个元素都是回调函数的结果,并且被展开一级。

替代方案

const arr = [1, 2, 3, 4];

arr.flatMap((x) => [x, x * 2]);
// 等价于
const n = arr.length;
const acc = new Array(n * 2);
for (let i = 0; i < n; i++) {
  const x = arr[i];
  acc[i * 2] = x;
  acc[i * 2 + 1] = x * 2;
}
// [1, 2, 2, 4, 3, 6, 4, 8]

需要注意的是,在这种特定情况下,flatMap 方法的性能比 for 循环要慢——因为需要创建临时数组并进行垃圾回收,同时返回的数组不需要经常调整大小。但是,在注重灵活性和可读性的情况下,flatMap 仍可能是正确的解决方案。

3.11.2 示例
1. map()与flatmap()
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"]

2. 稀疏数组使用flatmap()

callbackFn 不会被源数组中的空槽调用,因为 map() 不会调用,而 flat() 将忽略返回数组中的空槽。

console.log([1, 2, , 4, 5].flatMap((x) => [x, x * 2])); // [1, 2, 2, 4, 4, 8, 5, 10]
console.log([1, 2, 3, 4].flatMap((x) => [, x * 2])); // [2, 4, 6, 8]
3. 在非数组对象中使用

flatMap() 方法读取 thislength 属性,然后访问每个整数索引。如果回调函数的返回值不是数组,则始终直接将其附加到结果数组的末尾。

const arrayLike = {
  length: 3,
  0: 1,
  1: 2,
  2: 3,
};
console.log(Array.prototype.flatMap.call(arrayLike, (x) => [x, x * 2]));
// [1, 2, 2, 4, 3, 6]

// 回调函数返回的类数组对象不会被展平
console.log(
  Array.prototype.flatMap.call(arrayLike, (x) => ({
    length: 1,
    0: x,
  })),
);
// [ { '0': 1, length: 1 }, { '0': 2, length: 1 }, { '0': 3, length: 1 } ]

3.12 Array.prototype.forEach()🏳️

for循环 Each 每个 对数组的每个元素执行一次给定的函数。

3.12.1 语法

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

forEach(callbackFn)
forEach(callbackFn, thisArg)

callbackFn

为数组中每个元素执行的函数。并会丢弃它的返回值。该函数被调用时将传入以下参数:

  • element

    数组中正在处理的当前元素。

  • index

    数组中正在处理的当前元素的索引。

  • array

    调用 forEach() 的当前数组。

    thisArg

    在执行 callbackFn 时用作 this 的值

返回值

undefined

3.12.2 示例
1. 在稀疏数组上使用 forEach()

空槽不会执行forEach的callbackFn函数

const arraySparse = [1, 3, /* empty */, 7];
let numCallbackRuns = 0;

arraySparse.forEach((element) => {
  console.log({ element });
  numCallbackRuns++;
});

console.log({ numCallbackRuns });

// { element: 1 }
// { element: 3 }
// { element: 7 }
// { numCallbackRuns: 3 }

2. 使用 thisArg

下面的代码会为每一个数组元素输出一行记录:

class Counter {
  constructor() {
    this.sum = 0;
    this.count = 0;
  }
  add(array) {
    // 只有函数表达式才有自己的 this 绑定
    array.forEach(function countEntry(entry) {
      this.sum += entry;
      ++this.count;
    }, this);
  }
}

const obj = new Counter();
obj.add([2, 5, 9]);
console.log(obj.count); // 3
console.log(obj.sum); // 16
3. 扁平化数组 (使用递归)
const flatten = (arr) => {
  const result = [];
  arr.forEach((item) => {
    if (Array.isArray(item)) {
      result.push(...flatten(item));
    } else {
      result.push(item);
    }
  });
  return result;
};

// 用例
const nested = [1, 2, 3, [4, 5, [6, 7], 8, 9]];
console.log(flatten(nested)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
4. 非数组对象上使用forEach()

forEach() 方法读取 thislength 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
Array.prototype.forEach.call(arrayLike, (x) => console.log(x));
// 2
// 3
// 4

3.13 Array.prototype.includes()🏳️

includes 包含嘛 本质上就是看这个数组包不包含这个元素

3.13.1 语法

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

includes(searchElement)
includes(searchElement, fromIndex)

searchElement

需要查找的值。

fromIndex

开始搜索的索引(从零开始),会转换为整数。

  • 负索引从数组末尾开始计数——如果 fromIndex < 0,那么实际使用的是 fromIndex + array.length。然而在这种情况下,数组仍然从前往后进行搜索。
  • 如果 fromIndex < -array.length 或者省略 fromIndex,则使用 0,这将导致整个数组被搜索。
  • 如果 fromIndex >= array.length,则不会搜索数组并返回 false

返回值

一个布尔值,如果在数组中(或者在 fromIndex 所指示的数组部分中,如果指定 fromIndex 的话)找到 searchElement 值,则该值为 true

3.13.2 示例
1. 使用includes()
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
[1, 2, NaN].includes(NaN); // true
["1", "2", "3"].includes(3); // false
2. 在稀疏数组上使用includes()

你可以在稀疏数组中搜索 undefined,得到 true

console.log([1, , 3].includes(undefined)); // true
3. 在非数组对象中使用includes()

includes() 方法读取 thislength 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.includes.call(arrayLike, 2));
// true
console.log(Array.prototype.includes.call(arrayLike, 1));
// false

3.14 Array.prototype.indexOf()🏳️

index 索引 取索引用的 也就是数组的下标

3.14.1 语法

indexOf() 方法返回数组中第一次出现给定元素的下标,如果不存在则返回 -1。

他还有一个孪生兄弟

lastIndexOf() 方法返回数组中给定元素最后一次出现的索引,如果不存在则返回 -1。该方法从 fromIndex 开始向前搜索数组。

indexOf(searchElement)
indexOf(searchElement, fromIndex)
lastIndexOf(searchElement)
lastIndexOf(searchElement, fromIndex)

searchElement

数组中要查找的元素。

fromIndex

开始搜索的索引(从零开始),会转换为整数。

  • 负索引从数组末尾开始计数——如果 fromIndex < 0,那么实际使用的是 fromIndex + array.length。然而在这种情况下,数组仍然从前往后进行搜索。
  • 如果 fromIndex < -array.length 或者省略 fromIndex,则使用 0,这将导致整个数组被搜索。
  • 如果 fromIndex >= array.length,则不会搜索数组并返回 false

返回值

首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1

3.14.2 示例
1. 使用indexOf()
const array = [2, 9, 9];
array.indexOf(2); // 0
array.indexOf(7); // -1
array.indexOf(9, 2); // 2
array.indexOf(2, -1); // -1
array.indexOf(2, -3); // 0
// 你没法使用 indexOf() 来搜索 NaN。
const array = [NaN];
array.indexOf(NaN); // -1
2. 在稀疏数组上使用indexOf()

不能使用 indexOf() 在稀疏数组中搜索空槽。

console.log([1, , 3].indexOf(undefined)); // -1
3. 在非数组对象中使用indexOf()
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.indexOf.call(arrayLike, 2));
// 0
console.log(Array.prototype.indexOf.call(arrayLike, 5));
// -1

3.15 Array.prototype.join()🏳️

join 加入 将数组中的所有元素加入到一个字符串中 如果只有一个元素 那么就直接返回该元素

3.15.1 语法

join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串,用逗号或指定的分隔符字符串分隔。如果数组只有一个元素,那么将返回该元素而不使用分隔符。

join()
join(separator)

separator

指定一个字符串来分隔数组的每个元素。如果需要,将分隔符转换为字符串。如果省略,数组元素用逗号(,)分隔。如果 separator 是空字符串(""),则所有元素之间都没有任何字符。

返回值

一个所有数组元素连接的字符串。如果 arr.length0,则返回空字符串。

3.15.2 示例
1. 使用四种方式连接字符串
const a = ["Wind", "Water", "Fire"];
a.join(); // 'Wind,Water,Fire'
a.join(", "); // 'Wind, Water, Fire'
a.join(" + "); // 'Wind + Water + Fire'
a.join(""); // 'WindWaterFire'
2. 在稀疏数组上使用join()

join() 将空槽视为 undefined,并产生额外的分隔符:

console.log([1, , 3].join()); // '1,,3'
console.log([1, undefined, 3].join()); // '1,,3'
3. 在类数组对象中使用join()

join() 方法读取 thislength 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.join.call(arrayLike));
// 2,3,4
console.log(Array.prototype.join.call(arrayLike, "."));
// 2.3.4

3.16 Array.prototype.keys()🏳️

keys 也就是key的集合

3.16.1 语法

keys() 方法返回一个新的数组迭代器对象,其中包含数组中每个索引的键。

keys()

返回值

一个新的可迭代迭代器对象。

3.16.2 示例
1. 基础使用
const array1 = ['a', 'b', 'c'];
const iterator = array1.keys();

for (const key of iterator) {
  console.log(key);
}

// Expected output: 0
// Expected output: 1
// Expected output: 2
2. 在稀疏数组中使用 keys()

Object.keys() 只包含数组中实际存在的键不同,keys() 迭代器不会忽略缺失属性的键。

const arr = ["a", , "c"];
const sparseKeys = Object.keys(arr);
const denseKeys = [...arr.keys()];
console.log(sparseKeys); // ['0', '2']
console.log(denseKeys); // [0, 1, 2]
3. 在非数组对象上调用 keys()
const arrayLike = {
  length: 3,
};
for (const entry of Array.prototype.keys.call(arrayLike)) {
  console.log(entry);
}
// 0
// 1
// 2

3.17 Array.prototype.map()🏳️‍🌈

map 对数组的元素进行地图式的扫荡,每个元素都执行一遍函数,并把每个元素的函数返回值组成一个新的数组

3.17.1 语法

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

map(callbackFn)
map(callbackFn, thisArg)

callbackFn

为数组中的每个元素执行的函数。它的返回值作为一个元素被添加为新数组中。该函数被调用时将传入以下参数:

  • element

    数组中当前正在处理的元素。

  • index

    正在处理的元素在数组中的索引。

  • array

    调用了 map() 的数组本身。

thisArg

执行 callbackFn 时用作 this 的值。

返回值

一个新数组

3.17.2 示例
1. 求数组中每个元素的平方根
const numbers = [1, 4, 9];
const roots = numbers.map((num) => Math.sqrt(num));

// roots 现在是     [1, 2, 3]
// numbers 依旧是   [1, 4, 9]
2. 使用 map 重新格式化数组中的对象
const kvArray = [
  { key: 1, value: 10 },
  { key: 2, value: 20 },
  { key: 3, value: 30 },
];

const reformattedArray = kvArray.map(({ key, value }) => ({ [key]: value }));

console.log(reformattedArray); // [{ 1: 10 }, { 2: 20 }, { 3: 30 }]
console.log(kvArray);
// [
//   { key: 1, value: 10 },
//   { key: 2, value: 20 },
//   { key: 3, value: 30 }
// ]
3.稀疏数组上使用map

稀疏数组在使用 map() 方法后仍然是稀疏的。空槽的索引在返回的数组中仍然为空,并且回调函数不会对它们进行调用。

console.log(
  [1, , 3].map((x, index) => {
    console.log(`Visit ${index}`);
    return x * 2;
  }),
);
// Visit 0
// Visit 2
// [2, empty, 6]
4. 非数组对象上调用map

map() 方法读取 thislength 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.map.call(arrayLike, (x) => x ** 2));
// [ 4, 9, 16 ]

3.18 Array.prototype.pop()🏳️

很多语言都实现了这个pop 丢弃最后一个元素

3.18.1 语法

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

pop()

返回值

从数组中删除的元素(当数组为空时返回 undefined)。

3.18.2 示例
1. 删除数组中的最后一个元素
const myFish = ["angel", "clown", "mandarin", "sturgeon"];

const popped = myFish.pop();

console.log(myFish); // ['angel', 'clown', 'mandarin' ]

console.log(popped); // 'sturgeon'
2. 在非数组对象上调用 pop()

pop() 方法会读取 this 上的 length 属性。如果规范化的 length 属性为 0,length 会被再次设置为 0(鉴于之前可能是负数或者 undefined)。否则,返回并删除位于 length - 1 处的属性。

const arrayLike = {
  length: 3,
  unrelated: "foo",
  2: 4,
};
console.log(Array.prototype.pop.call(arrayLike));
// 4
console.log(arrayLike);
// { length: 2, unrelated: 'foo' }

const plainObj = {};
// 没有 length 属性,所以长度为 0
Array.prototype.pop.call(plainObj);
console.log(plainObj);
// { length: 0 }
3. 以类数组的方式使用对象

pushpop 方法是通用的,我们可以利用这一点来编写更灵活的代码——如以下示例所示。

请注意,在此示例中,我们不会创建数组来存储对象集合。相反,我们将集合存储在对象本身上,并在 Array.prototype.pushArray.prototype.pop 上使用 call 来欺骗这些方法,让它们认为我们正在处理一个数组。

const collection = {
  length: 0,
  addElements(...elements) {
    // 每次添加元素时
    // obj.length 都会自动增加

    // 返回 push 方法的返回值,即 length 属性的新值
    return [].push.call(this, ...elements);
  },
  removeElement() {
    // 每次移除元素时
    // obj.length 都会自动减少

    // 返回 pop 方法的返回值,即被移除的元素
    return [].pop.call(this);
  },
};

collection.addElements(10, 20, 30);
console.log(collection.length); // 3
collection.removeElement();
console.log(collection.length); // 2

3.19 Array.prototype.push()🏳️

push推入 放入 在后面放入元素

3.19.1 语法

push() 方法将指定的元素添加到数组的末尾,并返回新的数组长度。

push()
push(element0)
push(element0, element1)
push(element0, element1, /* … ,*/ elementN)

  • elementN

    添加到数组末尾的元素。

返回值

调用方法的对象的新 length 属性。

3.19.2 示例
1. 在数组中添加元素
const sports = ["soccer", "baseball"];
const total = sports.push("football", "swimming");

console.log(sports); // ['soccer', 'baseball', 'football', 'swimming']
console.log(total); // 4

2. 合并两个数组

使用展开运算符将第二个数组的所有元素添加到第一个数组中

const vegetables = ["parsnip", "potato"];
const moreVegs = ["celery", "beetroot"];

// 合并第二个数组到第一个数组中
vegetables.push(...moreVegs);

console.log(vegetables); // ['parsnip', 'potato', 'celery', 'beetroot']
3. 非数组对象上使用push()

push() 方法会读取 thislength 属性。然后,它将 this 的每个索引从 length 开始设置,并将参数传递给 push()。最后,它将 length 设置成之前的长度加上已添加元素的数量

const arrayLike = {
  length: 3,
  unrelated: "foo",
  2: 4,
};
Array.prototype.push.call(arrayLike, 1, 2);
console.log(arrayLike);
// { '2': 4, '3': 1, '4': 2, length: 5, unrelated: 'foo' }

const plainObj = {};
// 这里没有长度属性,所以长度为 0
Array.prototype.push.call(plainObj, 1, 2);
console.log(plainObj);
// { '0': 1, '1': 2, length: 2 }
4. 以类似数组的方式使用对象

如上所述,push 被有意设计为通用的,我们可以利用这一点。就像这个例子所展示的,Array.prototype.push 可以很好的处理对象。

注意,我们没有创建一个数组来存储对象集合。相反,我们将集合存储在对象本身上,并通过 Array.prototype.pushcall 来调用该方法,让它认为我们正在处理一个数组——归功于 JavaScript 允许我们以任何我们想要的方式建立执行上下文的方式,这样是可行的。

const obj = {
  length: 0,

  addElem(elem) {
    // obj.length 在每次添加元素时自动增加
    [].push.call(this, elem);
  },
};

// 让我们添加一些空对象来说明
obj.addElem({});
obj.addElem({});
console.log(obj.length); // 2

3.20 Array.prototype.reduce()🏳️‍🌈

数组中的每个元素从左到右进行缩减然后最后得到一个值

3.20.1 语法

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

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

reduce(callbackFn)
reduce(callbackFn, initialValue)

callbackFn

为数组中每个元素执行的函数。其返回值将作为下一次调用 callbackFn 时的 accumulator 参数。对于最后一次调用,返回值将作为 reduce() 的返回值。该函数被调用时将传入以下参数:

  • accumulator

    上一次调用 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 返回),则会抛出错误。

返回值

使用“reducer”回调函数遍历整个数组后的结果

边界情况

如果数组只有一个元素(无论位置如何)且未提供 initialValue,或者提供了 initialValue 但数组为空,则将返回该单个值,而调用 callbackFn

如果提供了 initialValue 且数组不为空,则 reduce 方法将始终从索引 0 开始调用回调函数。

如果未提供 initialValue,则对于长度大于 1、等于 1 和 0 的数组,reduce 方法将有不同的表现,如以下示例所示:

const getMax = (a, b) => Math.max(a, b);

// 从索引 0 开始为数组中的每个元素调用回调函数
[1, 100].reduce(getMax, 50); // 100
[50].reduce(getMax, 10); // 50

// 仅为索引 1 处的元素调用回调函数
[1, 100].reduce(getMax); // 100

// 不调用回调函数
[50].reduce(getMax); // 50
[].reduce(getMax, 1); // 1

[].reduce(getMax); // TypeError

3.20.2 示例
1. 无初始值时 reduce() 如何运行
const array = [15, 16, 17, 18, 19];

function reducer(accumulator, currentValue, index) {
  const returns = accumulator + currentValue;
  console.log(
    `accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`,
  );
  return returns;
}

array.reduce(reducer);

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

accumulatorcurrentValueindex返回值
第一次调用1516131
第二次调用3117248
第三次调用4818366
第四次调用6619485

array 参数在整个过程中始终不会改变——它始终是 [15, 16, 17, 18, 19]reduce() 返回的值将是最后一次回调返回值(85)。

2. 有初始值时 reduce() 如何运行

在这里,我们以相同的算法 reduce 同一个数组,但提供 10 作为 initialValue

[15, 16, 17, 18, 19].reduce(
  (accumulator, currentValue) => accumulator + currentValue,
  10,
);

回调函数会被调用五次,每次调用的参数和返回值如下表:

accumulatorcurrentValueindex返回值
第一次调用1015025
第二次调用2516141
第三次调用4117258
第四次调用5818376
第五次调用7619495

这种情况下 reduce() 返回的值是 95

3. 求对象数组中值的总和

为了对包含在对象数组中的值进行求和,必须提供一个 initialValue,以便每个项都通过回调函数处理。

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

console.log(sum); // 6
4. 展平嵌套数组
const flattened = [
  [0, 1],
  [2, 3],
  [4, 5],
].reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
// flattened 的值是 [0, 1, 2, 3, 4, 5]
5. 统计对象中值的出现次数
const people = [
  { name: "Alice", age: 21 },
  { name: "Max", age: 20 },
  { name: "Jane", age: 20 },
];

function groupBy(objectArray, property) {
  return objectArray.reduce((acc, obj) => {
    const key = obj[property];
    const curGroup = acc[key] ?? [];

    return { ...acc, [key]: [...curGroup, obj] };
  }, {});
}

const groupedPeople = groupBy(people, "age");
console.log(groupedPeople);
// {
//   20: [
//     { name: 'Max', age: 20 },
//     { name: 'Jane', age: 20 }
//   ],
//   21: [{ name: 'Alice', age: 21 }]
// }
6. 按属性对对象进行分组
const people = [
  { name: "Alice", age: 21 },
  { name: "Max", age: 20 },
  { name: "Jane", age: 20 },
];

function groupBy(objectArray, property) {
  return objectArray.reduce((acc, obj) => {
    const key = obj[property];
    const curGroup = acc[key] ?? [];

    return { ...acc, [key]: [...curGroup, obj] };
  }, {});
}

const groupedPeople = groupBy(people, "age");
console.log(groupedPeople);
// {
//   20: [
//     { name: 'Max', age: 20 },
//     { name: 'Jane', age: 20 }
//   ],
//   21: [{ name: 'Alice', age: 21 }]
// }
7. 数组去重
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);
8. 使用 reduce() 来替代 .filter().map()

使用 filter()map() 会遍历数组两次,但是你可以使用 reduce() 只遍历一次并实现相同的效果,从而更高效。(如果你喜欢使用 for 循环,你可以在遍历一次时使用 forEach() 进行过滤和映射。)

const numbers = [-5, 6, 2, 0];

const doubledPositiveNumbers = numbers.reduce((accumulator, currentValue) => {
  if (currentValue > 0) {
    const doubled = currentValue * 2;
    return [...accumulator, doubled];
  }
  return accumulator;
}, []);

console.log(doubledPositiveNumbers); // [12, 4]
9. 使用组合函数实现管道

管道(pipe) 是一种将多个函数组合在一起的技术,使得一个函数的输出可以作为下一个函数的输入。这种方式可以提高代码的可读性和可维护性,尤其是在处理数据转换和处理时。

// 组合使用的构建块
const double = (x) => 2 * x;
const triple = (x) => 3 * x;
const quadruple = (x) => 4 * x;

// 函数组合,实现管道功能
const pipe =
  (...functions) =>
  (initialValue) =>
    functions.reduce((acc, fn) => fn(acc), initialValue);

// 组合的函数,实现特定值的乘法
const multiply6 = pipe(double, triple);
const multiply9 = pipe(triple, triple);
const multiply16 = pipe(quadruple, quadruple);
const multiply24 = pipe(double, triple, quadruple);

// 用例
multiply6(6); // 36
multiply9(9); // 81
multiply16(16); // 256
multiply24(10); // 240
10.在稀疏数组上使用reduce()
console.log([1, 2, , 4].reduce((a, b) => a + b)); // 7
console.log([1, 2, undefined, 4].reduce((a, b) => a + b)); // NaN
11.在非数组对象中使用reduce()

reduce() 方法读取 thislength 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.reduce.call(arrayLike, (x, y) => x + y));
// 9

3.21 Array.prototype.reduceRight()🏳️

和reduce如出一辙 只不过从右往左计算

3.21.1 语法

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

reduceRight(callbackFn)
reduceRight(callbackFn, initialValue)

callbackFn

为数组中的每个元素执行的函数。其返回值将作为下一次调用 callbackFn 时的 accumulator 参数。对于最后一次调用,返回值将成为 reduceRight() 的返回值。该函数被调用时将传入以下参数:

  • accumulator

    上一次调用 callbackFn 的结果。在第一次调用时,如果指定了 initialValue 则为指定的值,否则为数组最后一个元素的值。

  • currentValue

    数组中当前正在处理的元素。

  • index

    正在处理的元素在数组中的索引。

  • array

    调用了 reduceRight() 的数组本身。

    initialValue

    首次调用 callbackFn 时累加器的值。如果不提供初始值,则将使用数组中的最后一个元素,并在迭代时跳过它。没有初始值的情况下,在空数组上调用 reduceRight() 会产生 TypeError

返回值

聚合后的结果值。

3.21.2 示例

和reduce的用法一样 这里不多做赘述了

1. 示例
const array1 = [
  [0, 1],
  [2, 3],
  [4, 5],
];

const result = array1.reduceRight((accumulator, currentValue) =>
  accumulator.concat(currentValue),
);

console.log(result);
// Expected output: Array [4, 5, 2, 3, 0, 1]

3.22 Array.prototype.reverse() 和toReverse()🏳️

reverse 反转

3.22.1 语法

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

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

reverse()
toReversed()

返回值

原始数组反转后的引用。注意,数组是就地反转的,并且没有复制。

3.22.2 示例
1. reverse() 方法返回对同一数组的引用

reverse() 方法返回对原始数组的引用,因此改变返回的数组也会改变原始数组。

const numbers = [3, 2, 4, 1, 5];
const reversed = numbers.reverse();
// numbers 和 reversed 的顺序都是颠倒的 [5, 1, 4, 2, 3]
reversed[0] = 5;
console.log(numbers[0]); // 5
2. 对稀疏数组使用 reverse()
console.log([1, , 3].reverse()); // [3, empty, 1]
console.log([1, , 3, 4].reverse()); // [4, 3, empty, 1]
3. 非数组对象上使用reverse()
const arrayLike = {
  length: 3,
  unrelated: "foo",
  2: 4,
};
console.log(Array.prototype.reverse.call(arrayLike));
// { '0': 4, length: 3, unrelated: 'foo' }
// 索引“2”被删除了,因为原本的数据中索引“0”不存在了

3.23 Array.prototype.shift()🏳️

shift 移动 移走第一个元素

3.23.1 语法

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

shift()

返回值

从数组中删除的元素;如果数组为空则返回 undefined

3.23.2 示例
1. 移除数组中的一个元素
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"
2. 在非数组对象上调用 shift()

shift 方法会读取 thislength 属性。如果规范化长度为 0,length 再次设置为 0(而之前可能为负值或 undefined)。否则,返回 0 处的属性,其余属性向左移动 1。length 属性递减 1。

const arrayLike = {
  length: 3,
  unrelated: "foo",
  2: 4,
};
console.log(Array.prototype.shift.call(arrayLike));
// undefined,因为它是一个空槽
console.log(arrayLike);
// { '1': 4, length: 2, unrelated: 'foo' }

const plainObj = {};
// 这里没有长度属性,所以长度为 0
Array.prototype.shift.call(plainObj);
console.log(plainObj);
// { length: 0 }

3.24 Array.prototype.slice()🏳️‍🌈

slice 切片

3.24.1 语法

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

slice()
slice(start)
slice(start, end)

start

提取起始处的索引(从 0 开始),会转换为整数。默认为0

  • 如果索引是负数,则从数组末尾开始计算——如果 start < 0,则使用 start + array.length
  • 如果 start < -array.length 或者省略了 start,则使用 0
  • 如果 start >= array.length,则不提取任何元素。

end

提取终止处的索引(从 0 开始),会转换为整数。slice() 会提取到但不包括 end 的位置。 默认为arr.length

  • 如果索引是负数,则从数组末尾开始计算——如果 end < 0,则使用 end + array.length
  • 如果 end < -array.length,则使用 0
  • 如果 end >= array.length 或者省略了 end,则使用 array.length,提取所有元素直到末尾。
  • 如果 end 在规范化后小于或等于 start,则不提取任何元素。

返回值

一个含有被提取元素的新数组。

3.24.2 示例
1. 返回现有数组的一部分
const fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
const citrus = fruits.slice(1, 3);

// fruits 包含 ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango']
// citrus 包含 ['Orange','Lemon']
2. 在类数组对象上调用 slice()

slice() 方法会读取 this 对象的 length 属性,然后从 startend 读取整数键属性,并将它们定义在一个新创建的数组中。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.slice.call(arrayLike, 1, 3));
// [ 3, 4 ]
3. 在稀疏数组上使用 slice()

如果源数组是稀疏数组,slice() 方法返回的数组也会是稀疏数组。

console.log([1, 2, , 4, 5].slice(1, 4)); // [2, empty, 4]

3.25 Array.prototype.sort()🏳️‍🌈

sort 排序

3.25.1 语法

sort() 方法就地对数组的元素进行排序,并返回对相同数组的引用。默认排序是将元素转换为字符串,然后按照它们的 UTF-16 码元值升序排序。他还有一个孪生兄弟,toSorted() 它可以创建一个排序后的副本,当然这个副本是浅拷贝的

sort()
sort(compareFn)
toSorted()
toSorted(compareFn)

compareFn

定义排序顺序的函数。返回值应该是一个数字,其符号表示两个元素的相对顺序:如果 a 小于 b,返回值为负数,如果 a 大于 b,返回值为正数,如果两个元素相等,返回值为 0NaN 被视为 0。该函数使用以下参数调用:

  • a

    第一个用于比较的元素。不会是 undefined

  • b

    第二个用于比较的元素。不会是 undefined

如果省略该函数,数组元素会被转换为字符串,然后根据每个字符的 Unicode 码位值进行排序。

compareFn(a, b) 返回值排序顺序
> 0ab 后,如 [b, a]
< 0ab 前,如 [a, b]
=== 0保持 ab 原来的顺序

返回值

经过排序的原始数组的引用。注意数组是就地排序的,不会进行复制。

3.25.2 示例
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);

// 根据 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;
});
2. 对象数组自定义排序
// 定义对象数组
const items = [
    { name: "apple", value: 1 },
    { name: "banana", value: 2 },
    { name: "cherry", value: 3 },
    { name: "date", value: 4 }
  ];
  
  // 定义排序顺序
  const order = ["banana", "apple", "cherry", "date"];
  
  // 自定义排序函数
  items.sort((a, b) => {
    return order.indexOf(a.name) - order.indexOf(b.name);
  });
  
  // 输出排序后的结果
  console.log(items);
//   [
//   { name: 'banana', value: 2 },
//   { name: 'apple', value: 1 },
//   { name: 'cherry', value: 3 },
//   { name: 'date', value: 4 }
// ]
3. 在稀疏数组上使用 sort()

空槽会被移动到数组的末尾。

console.log(["a", "c", , "b"].sort()); // ['a', 'b', 'c', empty]
console.log([, undefined, "a", "b"].sort()); // ["a", "b", undefined, empty]
4. 在类数组对象上调用 sort()

sort() 方法会读取 thislength 属性。然后它会收集在 0length - 1 范围内所有已存在的整数键属性,对它们进行排序,然后写回。如果范围内(即从 0 到 length - 1)存在缺失的属性,这些缺失的属性会被视为“不存在”,并在排序时被处理为排在末尾(就像稀疏数组一样)。

const arrayLike = {
  length: 3,
  unrelated: "foo",
  0: 5,//1不存在
  2: 4,
};
console.log(Array.prototype.sort.call(arrayLike));
// { '0': 4, '1': 5, length: 3, unrelated: 'foo' }

3.26 Array.prototype.splice()🏳️‍🌈

拼接 可删可加的神奇方法

3.26.1 语法

splice() 方法就地移除或者替换已存在的元素,并可以添加新的元素。

splice(start)
splice(start, deleteCount)
splice(start, deleteCount, item1)
splice(start, deleteCount, item1, item2)
splice(start, deleteCount, item1, item2, /* …, */ itemN)

start

从 0 开始计算的索引,表示要开始改变数组的位置,它会被转换成整数。

  • 负索引从数组末尾开始计算——如果 -buffer.length <= 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

如果 deleteCount0 或者负数,则不会移除任何元素。在这种情况下,你应该至少指定一个新元素(请参见下文)。

item1、…、itemN 可选

start 开始要加入到数组中的元素。

如果不指定任何元素,splice() 将只从数组中删除元素。

返回值

一个包含了删除的元素的数组。

如果只移除一个元素,则返回一个元素的数组。

如果没有删除任何元素,则返回一个空数组。

3.26.2 示例
1. 示例
const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// Inserts at index 1
console.log(months);
// Expected output: Array ["Jan", "Feb", "March", "April", "June"]

months.splice(4, 1, 'May');
// Replaces 1 element at index 4
console.log(months);
// Expected output: Array ["Jan", "Feb", "March", "April", "May"]
2. 在稀疏数组中使用 splice()

splice() 方法保留了数组的稀疏性。

const arr = [1, , 3, 4, , 6];
console.log(arr.splice(1, 2)); // [empty, 3]
console.log(arr); // [1, 4, empty, 6]

3. 在非数组对象中使用 splice()

splice() 方法读取 thislength 属性。然后,它根据需要更新整数键属性和 length 属性。

const arrayLike = {
  length: 3,
  unrelated: "foo",
  0: 5,
  2: 4,
};
console.log(Array.prototype.splice.call(arrayLike, 0, 1, 2, 3));
// [ 5 ]
console.log(arrayLike);
// { '0': 2, '1': 3, '3': 4, length: 4, unrelated: 'foo' }

3.27 Array.prototype[Symbol.iterator]()🏳️

3.27.1 语法

Array 实例的 [Symbol.iterator]() 方法实现了可迭代协议,允许数组被大多数期望可迭代对象的语法所使用,例如展开语法和 for...of 循环。它返回一个数组迭代器对象,该对象会产生数组中每个索引的值。

array[Symbol.iterator]()

返回值

Array.prototype.values() 相同的返回值:一个新的可迭代迭代器对象,它会生成数组中每个索引的值。

3.27.2 示例
1. 基础示例
const array1 = ['a', 'b', 'c'];
const iterator1 = array1[Symbol.iterator]();

for (const value of iterator1) {
  console.log(value);
}

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

const arr = ["a", "b", "c", "d", "e"];
const arrIter = arr[Symbol.iterator]();
console.log(arrIter.next().value); // a
console.log(arrIter.next().value); // b
console.log(arrIter.next().value); // c
console.log(arrIter.next().value); // d
console.log(arrIter.next().value); // e
2. 使用相同的函数处理字符串和字符串数组

因为字符串和数组都实现了可迭代协议,所以可以设计一个通用函数以相同的方式处理这两种输入。这比直接调用 Array.prototype.values() 更好,后者要求输入是一个数组,或者至少是一个具有这种方法的对象。

function logIterable(it) {
  if (typeof it[Symbol.iterator] !== "function") {
    console.log(it, "不可迭代。");
    return;
  }
  for (const letter of it) {
    console.log(letter);
  }
}

// 数组
logIterable(["a", "b", "c"]);
// a
// b
// c

// 字符串
logIterable("abc");
// a
// b
// c

// 数值
logIterable(123);
// 123 不可迭代。

3.28 Array.prototype.toLocaleString()🏳️

Array.prototype.toLocaleString() 是一个用于将数组转换为字符串的方法,且该字符串会根据用户的区域设置进行格式化

3.28.1 语法

toLocaleString() 方法返回一个字符串,表示数组中的所有元素。每个元素通过调用它们自己的 toLocaleString 方法转换为字符串,并且使用特定于语言环境的字符串(例如逗号“,”)分隔开。

toLocaleString()
toLocaleString(locales)
toLocaleString(locales, options)

locales

带有 BCP 47 语言标签的字符串,或者此类字符串的数组。对于 locales 参数的一般形式和说明,可以参见 Intl 主页面的参数说明

options

一个具有配置属性的对象。对于数字,请参见 Number.prototype.toLocaleString();对于日期,请参见 Date.prototype.toLocaleString()

返回值

一个字符串,表示数组中的所有元素。

3.28.2 示例
1. 使用 locales 和 options

始终显示 prices 数组中字符串和数字的货币符号:

const prices = ["¥7", 500, 8123, 12];
prices.toLocaleString("ja-JP", { style: "currency", currency: "JPY" });

// "¥7,¥500,¥8,123,¥12"

2. 在稀疏数组中使用 toLocaleString()

toLocaleString() 将空槽视为 undefined ,并生成一个额外的分隔符:

console.log([1, , 3].toLocaleString()); // '1,,3'
3. 在非数组对象中使用 toLocaleString()

toLocaleString() 方法读取 thislength 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 1,
  1: 2,
  2: 3,
};
console.log(Array.prototype.toLocaleString.call(arrayLike));
// 1,2,3

3.29 Array.prototype.toString()🏳️

转化为string

3.29.1 语法
toString()

返回值

一个表示数组元素的字符串。

3.29.2 示例
1. 使用toString()
const array1 = [1, 2, "a", "1a"];

console.log(array1.toString()); // "1,2,a,1a"

2. 在稀疏数组中使用 toString()

join() 的行为一致,toString() 将空槽视为 undefined 并生成一个额外的分隔符:

console.log([1, , 3].toString()); // '1,,3'
3. 在非数组对象中使用 toString()

toString() 是通用的。它期望 this 具有 join() 方法;如果不存在,则使用 Object.prototype.toString()

console.log(Array.prototype.toString.call({ join: () => 1 }));
// 1; 一个数字
console.log(Array.prototype.toString.call({ join: () => undefined }));
// undefined
console.log(Array.prototype.toString.call({ join: "not function" }));
// "[object Object]"

3.30 Array.prototype.unshift()🏳️

unshift 取消移位 不把内容放在后面 而是放在前面 与push刚好反着

3.30.1 语法

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

unshift()
unshift(element1)
unshift(element1, element2)
unshift(element1, element2, /* …, */ elementN)

element1、…、elementN

添加到 arr 开头的元素。

返回值

返回调用方法对象的新 length 属性。

3.30.2 示例
1. 使用 unshift()
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 ]

2. 在非数组对象中使用 unshift()
const arrayLike = {
  length: 3,
  unrelated: "foo",
  2: 4,
};
Array.prototype.unshift.call(arrayLike, 1, 2);
console.log(arrayLike);
// { '0': 1, '1': 2, '4': 4, length: 5, unrelated: 'foo' }

const plainObj = {};
// 这里没有长度属性,所以这里的长的为 0
Array.prototype.unshift.call(plainObj, 1, 2);
console.log(plainObj);
// { '0': 1, '1': 2, length: 2 }

3.31 Array.prototype.values()🏳️

values 数组的值

3.31.1 语法

values() 是专门为数组设计的,而 Symbol.iterator 是更通用的迭代器协议的一部分。实际上,Array.prototype.values() 实际上是调用 Array.prototype[Symbol.iterator]() 的一种方式,返回的迭代器在功能上是相同的。

values() 方法返回一个新的数组迭代器对象,该对象迭代数组中每个元素的值。跟Array.prototype[Symbol.iterator]() 是一样的

values()

返回值

一个新的可迭代迭代器对象。

3.31.2 示例

得到的返回值与Array.prototype[Symbol.iterator]()一样,这里不多做赘述

3.32 Array.prototype.with()🏳️

返回一个新的数组副本 并且附带(with)着一个修改的值

3.32.1 语法

Array 实例的 with() 方法是使用方括号表示法修改指定索引值的复制方法版本。它会返回一个新数组,其指定索引处的值会被新值替换。

arrayInstance.with(index, value)

index

要修改的数组索引(从 0 开始),将会转换为整数。

  • 负数索引会从数组末尾开始计数——即当 index < 0 时,会使用 index + array.length
  • 如果规范化后的索引超出数组边界,会抛出 RangeError

value

要分配给指定索引的任何值

返回值

一个全新的数组,其中 index 索引处的元素被替换为 value

3.32.2 示例
1. 创建一个新的数组,改变其中一个元素
const arr = [1, 2, 3, 4, 5];
console.log(arr.with(2, 6)); // [1, 2, 6, 4, 5]
console.log(arr); // [1, 2, 3, 4, 5]
2. 在稀疏数组上使用 with()

with() 方法总会创建一个密集数组。空的部分用undefined填充

const arr = [1, , 3, 4, , 6];
console.log('原数组',arr)
console.log(arr.with(0, 2)); 

// 原数组 [ 1, <1 empty item>, 3, 4, <1 empty item>, 6 ]
// [ 2, undefined, 3, 4, undefined, 6 ]
3. 在非数组对象上调用 with()

with() 方法创建并返回一个新数组。它读取 thislength 属性,然后访问其键是小于 length 的非负整数的每个属性。当 this 的每个属性被访问后,索引等于该属性的键的数组元素被设置为该属性的值。最后,将 index 的数组值设置为 value

const arrayLike = {
  length: 3,
  unrelated: "foo",
  0: 5,
  2: 4,
  3: 3, // 由于 length 属性的值为 3,with() 会忽略该值
};
console.log(Array.prototype.with.call(arrayLike, 0, 1));
// [ 1, undefined, 4 ]

如果以上教程有错误描述,欢迎来评论区批或者私信评指正

参考

1.Array - JavaScript | MDN (mozilla.org)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜业

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值