【重走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?)
正向查找,返回查找的元素的索引,没有返回-1lastIndexOf(searchElement, fromIndex?)
反向查找,返回查找的元素的索引,没有返回-1includes(searchElement, fromIndex?)
正向查找,是否至少找到一个与查找元素匹配的项,返回布尔值
断言函数y(允许使用搜索条件),以下方法接受三个参数,当前元素、索引和数组本身
find(callbackFn, thisArg?)
正向查找,第一个匹配的元素,找到匹配项后不再继续搜索。找不到返回undefinedfindIndex(callbackFn, thisArg?)
正向查找,第一个匹配的元素的索引,找到匹配项后不再继续搜索。找不到返回-1findLast(callbackFn, thisArg?)
反向查找,第一个匹配的元素,找到匹配项后不再继续搜索。找不到返回undefinedfindLastIndex(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的区别:
- map方法和forEach会修改原数组吗?
为什么网上查到有些说可以改,有些说不能改。其实都没有说错,但是也可以说他们都没理解。
底层原理均为先浅拷贝原数组进行操作,浅拷贝对基本数组类型和引用类型作用,最终的结果是不同的。(什么是深、浅拷贝)
若数组内是基本数据类型则无法改变原数组,若数组内是引用类型则可以改变原数组
- map方法和forEach的区别:
-
map需要return一个返回值作为结果,数组中的元素为原始数组调用函数处理后的值。map方法不会对空数组进行检测,如果遍历的是空数组会返回一个空数组
-
forEach方法没有返回值(undefined),forEach无论你return与否均返回undefined; 对于空数组时不会调用回调函数的;
- 对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()),但比分别调用这两个方法稍微更高效一些。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现