JavaScript 知识体系之数组基础

1,577 阅读11分钟

这是我参与更文挑战的第四天,活动详情查看:更文挑战

数组是 JavaScript 工作中最常用的数据类型之一,今天总结下各种场景下的数组方法,话不多说。show you the code

JavaScript 中的数组是什么

数组是一串连续的内存位置,用来保存某些值。注意重点,“连续”(continuous,或 contiguous),这很重要。

array-neicun-guanli

Array 的底层硬件实现由一个叫做内存管理器的东西,每当申请数组的时候--计算机实际上是在内存中开辟了一段连续的地址,每一个地址就可以直接通过内存管理器

上图中示例的就是数组相应的内存地址,如果访问数组中第一个元素或者数组最后一个元素--即访问数组中任意一个元素, 时间复杂度都为常数复杂度 O(1)

因为数组可以随机访问任何一个元素,所以它的访问时间还是比较快的,这是它的特征之一

创建数组

Array 构造函数

// 初始化空数组
const arr2 = new Array();

//初始化长度为7的数组
const arr3 = new Array(7);

// 创建包含N个元素的数组
const arr4 = new Array("red", "blue", "green");

// 通过fill填充元素
const arr5 = new Array(7).fill(1);

使用 fill() 填充元素的问题

const arr = new Array(7).fill({ a: 1 });

const arr = new Array(7).fill({ a: 1 });
console.log("---arr---beforeUpdate");
console.log(arr);

arr[4].a = 6;
console.log("---arr---");
console.log(arr);

打印出修改之前的 arr 及修改之后的 `arr``

通过 fill填充的数组元素,并且元素是引用类型时, 修改某一个元素会影响到其他的元素

字面量表示法

// 创建空数组
const arr = [];

// 创建包含N个元素的数组
const arr1 = [1, 2, 3, 4];

数组空位

使用数组字面量初始化数组时,可以使用一串逗号来创建空位(hole),ES6 标准明确将空位转为undefined

const options = [1, , , , 5];

for (const option of options) {
  console.log(option === undefined);
}
// false
// true
// true
// true
// false

Array.from()

作用:将类数组转化为数组实例

"类数组"对象本质是任何可迭代的结构,或者有一个length属性和可索引元素的结构。原生具备 Iterator 接口的数据结构有:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象
// 字符串会被拆分为单字符数组
console.log(Array.from("Matt")); // ["M", "a", "t", "t"]

// 可以使用from()将集合和映射转换为一个新数组 const m = new Map().set(1, 2)
const m = new Map().set(1, 2).set(3, 4);

const s = new Set().add(1).add(2).add(3).add(4);

console.log(Array.from(m)); // [[1, 2], [3, 4]]
console.log(Array.from(s)); // [1, 2, 3, 4]

Array.of()

// 把一组参数转化为数组

console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
console.log(Array.of(undefined)); // [undefined]

判断是否是数组类型

  1. instanceof
  2. constructor
  3. Object.prototype.isPrototypeOf
  4. getPrototypeOf
  5. Object.prototype.toString
  6. Array.isArray
let arr = [];
// 1. instanceof
arr instanceof Array;

// 2. constructor
arr.constructor === Array;

// 3. Object.prototype.isPrototypeOf
Array.prototype.isPrototypeOf(arr);

// 4. getPrototypeOf
Object.getPrototypeOf(arr) === Array.prototype;

// 5. Object.prototype.toString
Object.prototype.toString.call(arr) === "[object Array]";

// 6. Array.isArray ES6 新增- 推荐
Array.isArray(arr);

查找指定的数组元素

  • indexOf: 返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1
  • lastIndexOf: 返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找
  • includes: 用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false。
  • find: 返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
  • findIndex: 返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1
  • filter: 返回一个新数组, 其包含通过所提供函数实现的测试的所有元素

下面是一个查找数组中某个元素是否存在的例子,

const list = ["小王", "小李", "小杨", "小胡", "小高", "小赵", "小钱", "小孙"];

// 7
const res1 = list.indexOf("小孙");
// 7
const res2 = list.lastIndexOf("小孙");
// 小孙
const res3 = list.find((item) => item === "小孙");
// 7
const res4 = list.findIndex((item) => item === "小孙");
// ['小孙']
const res5 = list.filter((item) => item === "小孙");
// true
const res6 = list.some((item) => item === "小孙");
// true
const res7 = list.includes("小孙");

改变原有数组的方法

添加元素

  • push: 将一个或多个元素添加到数组的末尾,并返回该数组的新长度

添加一个对象到尾部的位置, 使用 Array.push.

let car = {
  color: "red",
  type: "cabrio",
  registration: new Date("2016-05-02"),
  capacity: 2,
};
cars.push(car);
  • unshit: 将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)

在头部添加一个对象, 使用 Array.unshift.

let car = {
  color: "red",
  type: "cabrio",
  registration: new Date("2016-05-02"),
  capacity: 2,
};
cars.unshift(car);
  • concat: 合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组
const array1 = ["a", "b", "c"];
const array2 = ["d", "e", "f"];
const array3 = array1.concat(array2);

console.log(array3);
// expected output: Array ["a", "b", "c", "d", "e", "f"]

删除元素

  • pop: 从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度
const plants = ["broccoli", "cauliflower", "cabbage", "kale", "tomato"];

console.log(plants.pop());
// expected output: "tomato"
  • shift:从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
const array1 = [1, 2, 3];

const firstElement = array1.shift();

console.log(array1);
// expected output: Array [2, 3]

console.log(firstElement);
// expected output: 1

从数组中间 添加/删除元素

  • splice(a,b,c): 通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
    • a:要截去的开始位置
    • b:截去的长度
    • c:要添加的元素

添加一个对象到中间位置, 使用 Array.splice. 此方法非常方便,因为它也可以删除数组的某一个元素。 注意其参数:

Array.splice(
  {index where to start},
  {how many items to remove},
  {items to add}
);

因此,如果我们要在第五个位置添加红色敞篷车,可以像下面一样使用:

let car = {
  color: "red",
  type: "cabrio",
  registration: new Date("2016-05-02"),
  capacity: 2,
};
cars.splice(4, 0, car);

迭代器方法

什么是数组的迭代器方法

对数组的每一个元素应用一个函数,可以返回一个值,一组值,一个新数组

不生成新数组的迭代器方法

  • forEach: 对数组的每个元素执行一次给定的函数
const array1 = ["a", "b", "c"];

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

// expected output: "a"
// expected output: "b"
// expected output: "c"
  • every:测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值
const isBelowThreshold = (currentValue) => currentValue < 40;

const array1 = [1, 30, 39, 29, 10, 13];

console.log(array1.every(isBelowThreshold));
// expected output: true
  • some:测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型
const array = [1, 2, 3, 4, 5];

// checks whether an element is even
const even = (element) => element % 2 === 0;

console.log(array.some(even));
// expected output: true

生成新数组的迭代器方法

  • slice():返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括 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"]
  • reduceRight:接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
const array1 = [
  [0, 1],
  [2, 3],
  [4, 5],
].reduceRight((accumulator, currentValue) => accumulator.concat(currentValue));

console.log(array1);
// expected output: Array [4, 5, 2, 3, 0, 1]
  • reduce:对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。

reducer 函数接收 4 个参数:

  1. Accumulator (acc) (累计器)
  2. Current Value (cur) (当前值)
  3. Current Index (idx) (当前索引)
  4. Source Array (src) (源数组)
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15

场景,

  • 累加,累乘

  • 数组转字符串

  • map: 创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值

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]
  • filter:创建一个新数组, 其包含通过所提供函数实现的测试的所有元素
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"]

其他

  • join():将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
const elements = ["Fire", "Air", "Water"];

console.log(elements.join());
// expected output: "Fire,Air,Water"

console.log(elements.join(""));
// expected output: "FireAirWater"

console.log(elements.join("-"));
// expected output: "Fire-Air-Water"
  • sort():对数组的元素进行排序,并返回数组.。默认排序顺序是在将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序.该方法会改变原数组
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]
  • reverse():将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组。
const array1 = ["one", "two", "three"];
console.log("array1:", array1);
// expected output: "array1:" Array ["one", "two", "three"]

const reversed = array1.reverse();
console.log("reversed:", reversed);
// expected output: "reversed:" Array ["three", "two", "one"]

// Careful: reverse is destructive -- it changes the original array.
console.log("array1:", array1);
// expected output: "array1:" Array ["three", "two", "one"]

实战演练

通过其值在数组中查找特定的对象 - Array.find

如果想在车辆数组中找到一辆红色的. 我们可以使用 Array.find.

let car = cars.find((car) => car.color === "red");

这个函数返回匹配到的第一个元素:

console.log(car);
// output:
// {
//   color: 'red',
//   type: 'station wagon',
//   registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
//   capacity: 5
// }

也可以搜索多个值:

let car = cars.find(car => car.color === "red" && car.type === "cabrio");

在这种情况下,我们将获得车辆列表中的最后一辆车.

从数组中获取符合条件的多个元素 - Array.filter

Array.find 方法只返回一个对象. 如果你想获取所有红色的车, 你需要使用 Array.filter.

let redCars = cars.filter((car) => car.color === "red");
console.log(redCars);
// output:
// [
//   {
//     color: 'red',
//     type: 'station wagon',
//     registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 5
//   },
//   {
//     color: 'red',
//     type: 'cabrio',
//     registration: 'Sat Mar 03 2012 01:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 2
//   }
// ]

转换数组的对象 - Array.map

在数组的所有方法中,Array.map方法可以说是我们使用最频繁的了:将对象数组转换为不同对象的数组。 这就是 Array.map 的工作。 假设我们要根据汽车的大小将其分为三类

let sizes = cars.map((car) => {
  if (car.capacity <= 3) {
    return "small";
  }
  if (car.capacity <= 5) {
    return "medium";
  }
  return "large";
});
console.log(sizes);
// output:
// ['large','medium','medium', ..., 'small']

如果我们需要更多的值,也可以创建一个新的对象:

let carsProperties = cars.map((car) => {
  let properties = {
    capacity: car.capacity,
    size: "large",
  };
  if (car.capacity <= 5) {
    properties["size"] = "medium";
  }
  if (car.capacity <= 3) {
    properties["size"] = "small";
  }
  return properties;
});
console.log(carsProperties);
// output:
// [
//   { capacity: 7, size: 'large' },
//   { capacity: 5, size: 'medium' },
//   { capacity: 5, size: 'medium' },
//   { capacity: 2, size: 'small' },
//   ...
// ]

向数组的每个对象添加属性 - Array.forEach

Array.map会生成新的数组。 那如果我们只想在原来的汽车对象做修改怎么办? 这对Array.forEach函数是一个很好的使用场景

cars.forEach((car) => {
  car["size"] = "large";
  if (car.capacity <= 5) {
    car["size"] = "medium";
  }
  if (car.capacity <= 3) {
    car["size"] = "small";
  }
});

按属性对数组排序 - Array.sort

完成对象转换后,通常需要以一种或另一种方式对它们进行排序。

通常,排序基于每个对象都具有的属性的值。 我们可以使用Array.sort函数,但是我们需要提供一个定义排序机制的函数(compareFunction)。

译者注:

如果指明了 compareFunction ,那么数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素:

  • 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
  • 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。备注: ECMAScript 标准并不>保证这一行为,而且也不是所有浏览器都会遵守(例如 Mozilla 在 2003 年之前的版本);
  • 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。
  • compareFunction(a, b) 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。

假设我们要根据汽车的降序对汽车进行排序。

let sortedCars = cars.sort((c1, c2) =>
  c1.capacity < c2.capacity ? 1 : c1.capacity > c2.capacity ? -1 : 0,
);
console.log(sortedCars);
// output:
// [
//   {
//     color: 'purple',
//     type: 'minivan',
//     registration: 'Wed Feb 01 2017 00:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 7
//   },
//   {
//     color: 'red',
//     type: 'station wagon',
//     registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 5
//   },
//   ...
// ]

如果排序函数的结果为正,则Array.sort比较两个对象,并将第一个对象放在第二位。 因此,你可以将排序函数视为一个问题:是否将第一个对象放置在第二个位置

确保两个对象的比较值相同时始终将大小比较写为零,以避免不必要的交换.

检查数组中的对象是否满足条件 - Array.every, Array.includes

当我们只需要检查每个对象的特定条件时,即可使用Array.everyArray.some

汽车清单上有红色敞篷车吗? 所有的汽车都能载至少 4 人吗?...

cars.some((car) => car.color === "red" && car.type === "cabrio");
// output: true

您可能还记得函数Array.includesArray.some类似,但只有元素是原始类型时候二者才类似


最后

文章浅陋,欢迎各位看官评论区留下的你的见解!

觉得有收获的同学欢迎点赞,关注一波!

20210531095857

往期文章