【重走JavaScript之高级程序设计】数组Array

Array

ECMA 规定数组是一组有序数据,和其他语言不同的是,数组中每个槽位可以存储任意类型的数据。

1. 创建数组

1.1 使用Array构造函数创建数组

// 使用Array构造函数创建数组
let colors = new Array(); // []
// 如果只传入一个参数,并且这个参数是数字,那么数组的长度就是这个数字
let colors = new Array(3); // (3) [empty × 3]
// 如果参数是其他类型或多个参数,那么数组就会依次赋值给这些参数
let colors = new Array("red"); // ["red"]
let colors = new Array(2, 3); // [2,3]
// 使用Array构造函数创建数组
let colors = Array(); // []

1.2 使用数组字面量创建数组(array literal)

// 使用数组字面量创建数组(与对象一样,使用数组字面量表示法创建数组不会调用Array构造函数)
let colors = [];
let colors = ["red", "green", "blue"];

1.3 ES6新增创建数组的静态方法

  • Array.from() 静态方法
// 字符串拆分为单字符数组
let happy = Array.from("Happy"); // ["H","a","p","p","y"]
// 对现有数组进行浅拷贝
let colors = Array.from(["red", "green", "blue"]);
// 将集合和映射转换为新数组
let m = new Map().set(1, 2).set(3, 4); // Map(2) {1 => 2, 3 => 4}
let s = new Set().add(1).add(2).add(3).add(4); // Set(4) {1, 2, 3, 4})
let arr1 = Array.from(m); // [1,2,3,4]
let arr2 = Array.from(m); // [1,2,3,4]
// 使用任意可迭代对象
const iter = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
};
Array.from(iter); // [1,2,3]
// arguments 对象转变为数组
function fn() {
  return Array.from(arguments);
}
fn(1, 2, 3, 4); // [1,2,3,4]
// 类数组对象
const arrLike = {
  0: "a",
  1: "b",
  2: "c",
  length: 3
};
Array.from(arrLike); // [1,2,3]

// Array.from() 接收第二个映射函数参数,类似于调用Array.from().map()。第三个参数制定映射函数中的值
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1, x => x ** 2); // [1,4,9,16]
const a3 = Array.from(
  a1,
  function (x) {
    return x ** this.exponent;
  },
  { exponent: 2 }
); // [1,4,9,16]
  • Array.of() 静态方法
// 使用Array.of()静态方法创建数组
let colors = Array.of("red", "green", "blue");

2. 数组索引

中括号提供的数字索引表示要访问的值。

数组的属性length 代表数组的元素数量。始终返回0或大于0的数值。

提供的小于元素长度返回对应索引的元素,大于元素长度则数组自动扩充到提供的长度

// 数组索引
let colors = ["red", "green", "blue"];
colors;
colors[0]; // "red"
colors[1]; // "green"
colors[2]; // "blue"
colors[3]; // undefined

数组的length属性不是只读的,可以通过修改length属性来从数组末尾添加或删除元素

let colors = ["red", "green", "blue"];
colors.length = 2;
colors[2]; // undefined

let colors = ["red", "green", "blue"];
cols.length = 5;
cols; //	["red", "green", "blue", empty × 2]

数组最后一个索引始终是length-1, 因此新增槽位的索引就是length

let colors = ["red", "green", "blue"];
colors[colors.length] = "yellow";
colors[colors.length] = "black";
colors; // ["red", "green", "blue", "yellow", "black"]

3. 数组空位

ES6之后的方法将空位视为undefined,而ES6之前的方法会忽视这个空位,所以不用使用空位会造成困惑,坚持用请显式的undefined来代替空位

// 使用一串逗号可以来创建空位
const options = [, , , ,]; // 创建包含五个元素的数组
options.length; // 5
options; // [empty × 5]

4.1 数组方法 _ 检测数组

单框架网页使用 instanceof 判断数组,ES6提供 Array.isArray() 方法来判断

// 检测数组
let colors = ["red", "green", "blue"];
colors instanceof Array; // true
Array.isArray(colors); // true

4.2 数组方法 _ 迭代器方法

ES6 提供了三种 返回数组身上迭代器的方法

  • keys() 返回数组索引的迭代器
  • values() 返回数组元素的迭代器
  • entries() 返回索引/值对的迭代器
// 迭代器方法,并使用Array.from()将迭代器转换为数组
let colors = ["red", "green", "blue"];
Array.from(colors.keys()); // [0, 1, 2]
Array.from(colors.values()); // ["red", "green", "blue" ]
Array.from(colors.entries()); // [[0, "red"], [1, "green"], [2, "blue"]]

for (const [index, element] of colors.keys()) {
  console.log(index); // 0, 1, 2
  console.log(element); // red, green, blue
}

4.3 数组方法 _ 复制和填充方法

以下两个方法,包含开始索引,不包含结束索引,破坏性更改,返回修改后的数组,但是不会改变数组的长度。

  • copyWithin(target, start?, end?) 批量复制方法
  • fill(value, start?, end?) 填充数组方法,向数组中插入全部或部分相同的值。
// 填充方法
const arr = [0, 0, 0, 0, 0];
// 用5填充全部数组
arr.fill(5); // [5, 5, 5, 5, 5]
arr.fill(0); // [0, 0, 0, 0, 0]
// 从索引3开始,用6填充数组
arr.fill(6, 3); // [0, 0, 0, 6, 6]
arr.fill(0); // [0, 0, 0, 0, 0]
// 用7填充索引大于等于1且小于3的元素
arr.fill(7, 1, 3); // [0, 0, 7, 7, 0]
arr.fill(0); // [0, 0, 0, 0, 0]
// 用8填充所以大于1切小小于4的元素
arr.fill(8, -4, -1); // [0, 8, 8, 8, 0]
// 索引超出数组边界、索引反向,静默忽略
arr.fill(1, -10, -6); // [0, 0, 0, 0, 0] 索引过低,忽略
arr.fill(1, 10, 15); // [0, 0, 0, 0, 0] 索引过高,忽略
arr.fill(2, 4, 2); // [0, 0, 0, 0, 0] 索引反向,忽略
arr.fill(4, 3, 10); // [0, 0, 0, 4, 4] 索引部分可用,填充部分
// 复制和填充方法
let arr = [0,1,2,3,4,5,6,7,8,9];
arr.copyWithin(5); // [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
arr.copyWithin(0,5); // [5, 6, 7, 8, 9, 5, 6, 7, 8, 9]
arr.copyWithin(4,0,3)	// [0, 1, 2, 3, 0, 1, 2, 7, 8, 9]
// 索引超出数组边界、索引反向,静默忽略

4.4 数组方法 _ 转换字符串方法

数组转换为字符串的方法

  • valueOf() 返回数组本身
  • toString() 数组转为字符串,逗号分隔
  • toLocaleString() 数组转为字符串,逗号分隔,对每个值调用的是toLocaleString()方法
  • join(separator?) 数组转为字符串,接收一个参数,指定字符串分隔符,默认逗号.String.split()的逆操作

4.5 数组方法 _ 栈和队列方法

栈是一种后进先出的数据结构(LIFO)

  • push() 推入,推入数组末尾,接收任意参数
  • pop() 弹出,删除最后一项同时返回被删除项

队列是一种先进先出的数据结构(FIFO)

  • unshift() 推入数组开头,接收任意参数
  • shift() 删除第一位同时返回被删除项

4.6 数组方法 _ 排序方法

以下两个方法均返回数组的引用,会改变原数组

  • reverse() 反转数组,不灵活。要在不改变原始数组的情况下反转数组中的元素,使用 toReversed()。
  • sort(compareFn) 排序,默认升序,可接收一个函数作为参数,用于排序。如果想要不改变原数组的排序方法,可以使用 toSorted()。
// 反转数组
let arr = [1, 2, 3, 4, 5];
arr.reverse(); // [5, 4, 3, 2, 1]
// 排序方法
let arr = [1, 2, 3, 4, 5];
arr.sort(); // [1, 2, 3, 4, 5] 不传参对每项调用String()比较字符串
arr.sort((a, b) => a - b); // 升序
arr.sort((a, b) => b - a); // 降序
arr.sort((a, b) => Math.random() - Math.random()); // 随机排序

4.7 数组方法 _ 操作方法

  • concat(...array?) 合并数组,接收任意数量的参数,返回一个新数组,不影响原数组
  • slice(start, end?) 截取数组,接收起始索引和结束索引(不包含),返回一个新数组,不影响原数组
  • splice(start, deleteCount, ...item?) 数组中间插入元素,最强大的数组方法,共三个参数,删除的索引、删除的数量、要插入的元素(任意个),返回一个新数组,会影响原数组
// splice是最强大的数组方法
// 删除
let arr = [1, 2, 3, 4, 5];
arr.splice(0, 1); // 返回[1]	原数组[2, 3, 4, 5]
// 插入
let arr = [1, 2, 3, 4, 5];
arr.splice(1, 0,99); //  返回[]	原数组[1, 99, 2, 3, 4, 5]
// 替换
let arr = [1, 2, 3, 4, 5];
arr.splice(1, 1,99); // 返回[2]	原数组[1, 99, 3, 4, 5]

4.8 数组方法 _ 搜索和位置方法

搜索方法分为两种,严格相等和按断言函数搜索

严格相等(===),以下方法均接受两个参数,第一个参数要查找的元素,第二个参数是开始查找的索引(可选)

  • indexOf(searchElement, fromIndex?) 正向查找,返回查找的元素的索引,没有返回-1
  • lastIndexOf(searchElement, fromIndex?) 反向查找,返回查找的元素的索引,没有返回-1
  • includes(searchElement, fromIndex?) 正向查找,是否至少找到一个与查找元素匹配的项,返回布尔值

断言函数y(允许使用搜索条件),以下方法接受三个参数,当前元素、索引和数组本身

  • find(callbackFn, thisArg?) 正向查找,第一个匹配的元素,找到匹配项后不再继续搜索。找不到返回undefined
  • findIndex(callbackFn, thisArg?) 正向查找,第一个匹配的元素的索引,找到匹配项后不再继续搜索。找不到返回-1
  • findLast(callbackFn, thisArg?) 反向查找,第一个匹配的元素,找到匹配项后不再继续搜索。找不到返回undefined
  • findLastIndex(callbackFn, thisArg?) 反向查找,第一个匹配的元素的索引,找到匹配项后不再继续搜索。找不到返回-1

4.9 数组方法 _ 迭代方法

迭代方法是指遍历完整个数组。

这些方法不改变调用他们的数组(forEach要分情况)。callbackFn接受三个参数,数组元素、元素索引和数组本身

  • forEach(callbackFn, thisArg?) 遍历数组,对数组每一项都执行传入的函数,没有返回值(return无效)
  • map(callbackFn, thisArg?) 映射一个新数组,对数组每一项都执行传入的函数,返回由每次函数调用返回值组成的数组
  • filter(callbackFn, thisArg?) 过滤,对数组每一项都执行传入的函数,函数返回true时的项组成的数组
  • every(callbackFn, thisArg?) 每一项都满足,对数组每一项都执行传入的函数,如果每一项都返回true,则返回true,否则返回false。
  • some(callbackFn, thisArg?) 至少一项满足,对数组每一项都执行传入的函数,如果有一项返回true,则 返回true,否则返回false。

思考问题 map方法和forEach的区别:

  1. map方法和forEach会修改原数组吗?

为什么网上查到有些说可以改,有些说不能改。其实都没有说错,但是也可以说他们都没理解。

底层原理均为先浅拷贝原数组进行操作,浅拷贝对基本数组类型和引用类型作用,最终的结果是不同的。(什么是深、浅拷贝)

若数组内是基本数据类型则无法改变原数组,若数组内是引用类型则可以改变原数组

  1. map方法和forEach的区别:
  • map需要return一个返回值作为结果,数组中的元素为原始数组调用函数处理后的值。map方法不会对空数组进行检测,如果遍历的是空数组会返回一个空数组

  • forEach方法没有返回值(undefined),forEach无论你return与否均返回undefined; 对于空数组时不会调用回调函数的;

  1. 对map和forEach的理解:

既然底层类似,为什么要设计这两种方法。即设计之初,对这两个函数的定位是不同。我的理解是时对待这两个方法应该从语义上去理解,高阶函数。

map重在强调映射(浅拷贝)出的新数组进行操作,而forEach重在强调处理的这个过程。

什么语义下用forEach,我想通过遍历数组中的每一项来做某一件事,强调处理

什么语义下用map,我想通过遍历数组中的每一项以得到一个新数组,强调映射得到新数组,可以进行链式调用


4.10 数组方法 _ 归并方法

归并:迭代数组的所有项,并在此基础上构建一个最终返回值。

callbackFn接受四个参数 accumulator,currentValue,currentIndex,array,还有一个可选参数初始值initialValue

  • reduce (callbackFn, initialValue?) 按照升序归并,累加器accumulator每次迭代传递,最后归并返回一个值。
  • reduceRight (callbackFn, initialValue?) 按照降序归并,累加器accumulator每次迭代传递,最后归并返回一个值。

4.11 数组方法 _ 打平数组方法

  • flat(depth?) 数组扁平化,指定要提取嵌套数组的结构深度,默认值为 1。

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

posted @ 2022-09-11 22:28  wanglei1900  阅读(24)  评论(0编辑  收藏  举报