JS Array专项复习1 ——数组及数组的静态方法
最近工作太忙😭,好久不更新了,趁着10月国庆假期复习一下JS的Array的一些方法,这些Array的方法对于前端开发者做数据处理以及做一些算法设计还是很关键的,当然稍微接触过前端JS的都懂,但是容易忘,本文旨在使用好记的方法来延长Array的记忆。并且简洁易懂,这是第一篇,静态方法
1. 何为Array(数组)❓
与其他编程语言中的数组一样,Array
对象支持在单个变量名下存储多个元素,并具有执行常见数组操作的成员。
1.1 创建数组的方式
大概可以分为创建空实例,通过元素创建,通过多个元素创建,以及通过数组长度创建。带new不带new都行。
let Array1 = new Array()
// console.log(Array1) 输出: []
// 使用元素初始化数组 但是如果元素是数字那么就会变成Array5
let Array2 = new Array('element')
// console.log(Array2) 输出: ['element']
// 使用0和1初始化数组
let Array3 = new Array(0, 1)
// console.log(Array3) 输出: [0, 1]
// 使用0, 1, ..., N初始化数组
let Array4 = new Array(0, 1, /* … ,*/ 5)
// console.log(Array4) 输出: [0, 1, 5]
// 指定数组长度
let Array5 = new Array(5)
// console.log(Array5) 输出: [ <5 empty items> ] 这个属于是创建了五个空槽的数组 他不是undefined 而是空槽 直接打印其中的元素是undefined 而如果使用数组迭代方法时 空槽是会跳过的
let Array6 = Array()
// console.log(Array6) 输出: []
// 使用元素初始化数组 但是如果元素是数字那么就会变成Array10
let Array7 = Array('element')
// console.log(Array7) 输出:Array('element')
// 使用0和1初始化数组
let Array8 = Array(0, 1)
// console.log(Array8) 输出: [0, 1]
// 使用0, 1, ..., N初始化数组
let Array9 = Array(0, 1, /* … ,*/ 5)
// console.log(Array9) 输出: [0, 1,5]
// 指定数组长度
let Array10 = Array(5)
// console.log(Array10) 输出: [ <5 empty items> ]
调用
Array()
时可以使用或不使用new
。两者都会创建一个新的Array
实例
也可以通过字面量(开发者直接给出的固定的值)直接创建
const fruits = ["Apple", "Banana"];
console.log(fruits.length); // 2
console.log(fruits[0]); // "Apple"
2. 数组静态方法
有人可能问了啥叫静态方法🤔,我这里稍微解释一下静态方法和实例方法的区别
定义方式:
静态方法:使用 static 关键字定义。它属于类本身,而不是类的实例。
实例方法:不使用 static 关键字定义。它属于类的实例。
调用方式:
静态方法:可以通过类名直接调用,而不需要创建类的实例。例如:ClassName.staticMethod()
实例方法:需要通过类的实例来调用。例如:instanceName.instanceMethod()
访问权限:
静态方法:只能访问类的静态变量和其他静态方法,不能直接访问实例变量或实例方法。
实例方法:可以访问类的所有变量和方法,包括静态和实例的。
使用场景:
静态方法:通常用于不依赖于实例变量的方法,或者是工具类方法。
实例方法:用于需要访问或修改实例变量的方法。
2.1 Array.from()🏳️
from嘛,最早我使用这个单词是I come from China. 我是中国来的,没错from这个数组静态方法就是告诉开发者,我这个数组是从哪里造出来的
const China = 'china'
const arrayFromChina = Array.from(China)
console.log(arrayFromChina) // 输出:[ 'c', 'h', 'i', 'n', 'a' ]
Array.from()这个静态方法可以从可迭代或者类数组对象创建一个新的浅拷贝的数组实例。
ok,有人可能不理解或者不清楚啥叫可迭代或者类数组对象。
可迭代对象
可迭代对象是指实现了 Symbol.iterator 方法的对象。这些对象可以被迭代,例如通过 for…of 循环。常见的可迭代对象包括:
数组:如 [1, 2, 3]
字符串:如 “hello”
Set:如 new Set([1, 2, 3])
Map:如 new Map([[‘key1’, ‘value1’], [‘key2’, ‘value2’]])
TypedArray:如 new Uint8Array([1, 2, 3])
这些对象都可以通过 Array.from() 方法转换为数组,因为它们都实现了迭代器协议。
类数组对象
类数组对象是指具有 length 属性和索引属性的对象,但不一定具有数组的方法。类数组对象通常不具备数组的原型方法(如 push、pop 等)。常见的类数组对象包括:
arguments 对象:函数内部的 arguments 对象
DOM 方法返回的 NodeList:如 document.querySelectorAll(‘div’)
自定义对象:如 { 0: ‘a’, 1: ‘b’, length: 2 }
类数组对象可以通过 Array.from() 转换为真正的数组,从而可以使用数组的方法。
2.1.1 语法
Array.from(arrayLike, mapFn, thisArg)
arrayLike
想要转换成数组的类数组或可迭代对象。
mapFn
调用数组每个元素的函数。如果提供,每个将要添加到数组中的值首先会传递给该函数,然后将 mapFn
的返回值增加到数组中。使用以下参数调用该函数:
-
element
数组当前正在处理的元素。
-
index
数组当前正在处理的元素的索引。
thisArg
执行 mapFn
时用作 this
的值。(这个玩意几乎很少用,如果非要搞懂怎么用,最后请看示例6)
返回值
一个Array
实例。
2.1.2 示例
1. 从字符串中构建数组
Array.from("foo");
// [ "f", "o", "o" ]
2. 从set中构建数组
const set = new Set(["foo", "bar", "baz", "foo"]);
Array.from(set);
// [ "foo", "bar", "baz" ]
3. 从map中构建数组
const map = new Map([
[1, 2],
[2, 4],
[4, 8],
]);
Array.from(map);
// [[1, 2], [2, 4], [4, 8]]
const mapper = new Map([
["1", "a"],
["2", "b"],
]);
Array.from(mapper.values());
// ['a', 'b'];
Array.from(mapper.keys());
// ['1', '2'];
4. 从nodelist中创建数组
// 根据 DOM 元素的属性创建一个数组
const images = document.querySelectorAll("img");
const sources = Array.from(images, (image) => image.src);
const insecureSources = sources.filter((link) => link.startsWith("http://"));
上面代码目的是从网页中提取所有 元素的 src 属性,并过滤出那些使用不安全的 HTTP 协议的图片链接。
5. 序列生成器
// 序列生成器函数(通常称为“range”,例如 Clojure、PHP 等)
const range = (start, stop, step) =>
Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step);
// 上面的意思是 先生成一个对象 对象为一个键值对 键为length 数值为数组的长度 然后mapFn的作用是将每个元素的值赋值为start+元素的索引值
// 生成的数字范围 0..4
range(0, 4, 1);
// [0, 1, 2, 3, 4]
// 生成的数字范围 1..10,步长为 2
range(1, 10, 2);
// [1, 3, 5, 7, 9]
// 使用 Array.from 生成字母表,并将其序列排序
range("A".charCodeAt(0), "Z".charCodeAt(0), 1).map((x) =>
String.fromCharCode(x),
);
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
6.使用thisArg的情况
// 定义一个对象,包含一个方法用于映射
const multiplier = {
factor: 2,
multiply(value) {
return value * this.factor;
}
};
// 类数组对象
const arrayLike = { 0: 1, 1: 2, 2: 3, length: 3 };
// 使用 Array.from() 将类数组对象转换为数组,并使用 multiplier.multiply 作为 mapFn
const resultArray = Array.from(arrayLike, multiplier.multiply, multiplier);
console.log(resultArray); // 输出: [2, 4, 6]
7.进阶:在非数组构造函数上调用 from()
要是看不懂也没关系,以后多接触接触就懂了,本质上就是换了个构造函数
function NotArray(len) {// NotArray 是一个构造函数,它接受一个参数 len,并在控制台输出调用时的长度。
console.log("NotArray called with length", len);
}
// 可迭代对象
//Array.from.call(NotArray, ...) 通过 call 方法将 Array.from 的上下文设置为 NotArray,这意味着 Array.from 将使用 NotArray 作为构造函数来创建新对象而不是使用Array的构造函数创建数组。
console.log(Array.from.call(NotArray, new Set(["foo", "bar", "baz"])));
//输出显示 NotArray 被调用时,len 是 undefined,因为 Set 没有 length 属性。
// NotArray called with length undefined
// NotArray { '0': 'foo', '1': 'bar', '2': 'baz', length: 3 }
// 这个对象仍然具有类似数组的结构(即索引属性和 length 属性),但它的原型链上是 NotArray,而不是 Array。
// 类数组对象
console.log(Array.from.call(NotArray, { length: 1, 0: "foo" }));
// NotArray called with length 1
// NotArray { '0': 'foo', length: 1 }
//当 this 值不是构造函数,返回一个普通的数组对象。
console.log(Array.from.call({}, { length: 1, 0: "foo" })); // [ 'foo' ]
2.2 Array.fromAsync()🏴☠️
这个玩意看看就好,几乎很少用。不过了解一下也算是与未来接轨了,正常使用需要下载 array-from-async,或者像我示例中的那样手动实现fromAsync的方法加入到Array的原型链中。array-from-async - npm (npmjs.com)
npm install array-from-async
…then:
import fromAsync from 'array-from-async';
2.2.1 语法
Array.fromAsync(arrayLike)
Array.fromAsync(arrayLike, mapFn)
Array.fromAsync(arrayLike, mapFn, thisArg)
-
arrayLike
要转换为数组的异步可迭代、可迭代或类数组对象。
-
mapFn
为数组中的每个元素执行的函数。如果提供了该函数,则每个要添加到数组中的值都会先通过该函数处理,
mapFn
的返回值将代替该值被添加到数组中(在等待兑现后)。该函数被调用时将传入以下参数:element
数组中当前正在处理的元素。由于所有元素都会先等待其兑现,因此该值永远不会是thenable。index
正在处理的元素在数组中的索引。 -
thisArg
执行
mapFn
时用作this
的值。
返回值
一个新的 Promise
,其兑现值是一个新的 Array
实例。
2.2.2 示例
正如介绍中说的那样,Array.fromAsync()还在草案阶段,这个方法是不存在于JavaScript标准库的,直接用会报错,我们借用第三方库的fromAsync加入到Array的原型链中。
[!WARNING]
Array.fromAsync() 示例中所有的代码前面都要加上这个代码
import fromAsync from 'array-from-async';
// 检查 Array 对象上是否已经存在 fromAsync 方法
if (!Array.fromAsync) {
// 将 fromAsync 方法添加到 Array 的原型链上
Array.fromAsync = fromAsync;
}
1. 来自异步可迭代数组
const asyncIterable = (async function* () {
for (let i = 0; i < 5; i++) {
await new Promise((resolve) => setTimeout(resolve, 10 * i));
yield i;
}
})();//这是一个异步生成器函数,定义后立即执行,返回一个异步可迭代对象 asyncIterable。
Array.fromAsync(asyncIterable).then((array) => console.log(array));
// [0, 1, 2, 3, 4]
总共花费五秒多
[Running] node “e:\qGraduation\server\test.js”
[ 0, 1, 2, 3, 4 ]
[Done] exited with code=0 in 5.784 seconds
2. 来自同步迭代函数
Array.fromAsync(
new Map([
[1, 2],
[3, 4],
]),
).then((array) => console.log(array)); // 返回一个promise立即执行打印操作
// [[1, 2], [3, 4]]
3. 来自同步迭代的数组,产生Promise
// 创建了一个 Set,其中包含三个 Promise 对象。
// 每个 Promise 都会立即解析为一个数字:1、2 和 3。
// Array.fromAsync 遍历 Set 中的每个 Promise,等待它们解析。
// 解析后的值 1、2 和 3 被收集到结果数组中。
Array.fromAsync(
new Set([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]),
).then((array) => console.log(array));
// [1, 2, 3]
4. 来自 Promise 的类数组对象的数组
Array.fromAsync()
内部会等待 mapFn
的输入和输出的兑现
Array.fromAsync({
length: 3,
0: Promise.resolve(1),
1: Promise.resolve(2),
2: Promise.resolve(3),
}).then((array) => console.log(array));
// [1, 2, 3]
// 这个应该很好理解 与可迭代对象不同 这是一个类数组对象
2.2.3 与 Promise.all() 的比较
Array.fromAsync()
会依次等待对象中产生的每个值兑现。Promise.all()
会并行等待所有值兑现。
// 定义一个生成器函数 每过100ms 生成一个数值
function* makeAsyncIterable() {
for (let i = 0; i < 5; i++) {
yield new Promise((resolve) => setTimeout(resolve, 100));
}
}
(async () => {
console.time("Array.fromAsync() time");
await Array.fromAsync(makeAsyncIterable());
console.timeEnd("Array.fromAsync() time");
// Array.fromAsync() time: 542.493ms
console.time("Promise.all() time");
await Promise.all(makeAsyncIterable());
console.timeEnd("Promise.all() time");
// Promise.all() time: 104.266ms
})();
2.2.4 没有对同步可迭代对象的错误处理(进阶)
// 定义一个生成器函数 generatorWithRejectedPromises
function* generatorWithRejectedPromises() {
try {
// 第一次 yield,生成值 0
yield 0;
// 第二次 yield,生成一个被拒绝的 Promise,拒绝值为 3
yield Promise.reject(3);
} finally {
// finally 块,通常用于清理操作
// 由于生成器被中断,且没有被显式关闭,这段代码不会执行
console.log("called finally");
}
}
// 异步自执行函数,用于处理生成器
(async () => {
try {
// 使用 Array.fromAsync 处理生成器
// 假设 Array.fromAsync 是一个存在的方法,用于将异步可迭代对象转换为数组
await Array.fromAsync(generatorWithRejectedPromises());
} catch (e) {
// 捕获 Promise 的拒绝错误,并输出错误信息
console.log("caught", e);
}
})();
// 输出: caught 3
// 没有“called finally”信息,因为生成器在 Promise.reject(3) 处被中断
但是我们可以使用for… of
for…of 循环在处理生成器时,如果循环被中断(例如通过 break、return、throw 或 await 抛出异常),它会自动调用生成器的 return() 方法。这是 JavaScript 的一个特性,旨在确保生成器的清理代码(如 finally 块)能够执行。
(async () => {
const arr = [];
try {
for (const val of generatorWithRejectedPromises()) {
arr.push(await val);
}
} catch (e) {
console.log("caught", e);
}
})();
// 输出: caught 3
// 有“called finally”信息,因为生成器在 Promise.reject(3) 处被中断后 for...of调用了生成器的return()方法
2.3 Array.isArray() 🏳️
显而易见,这玩意是判定是不是一个数组的。😀
2.3.1 语法
Array.isArray(value)
value
需要检测的值。
返回值
如果 value
是 Array
,则为 true
;否则为 false
。如果 value
是 TypedArray
实例,则总是返回 false
。
Array.isArray() 检查传递的值是否为 Array。它不检查值的原型链,也不依赖于它所附加的 Array 构造函数。对于使用数组字面量语法或 Array 构造函数创建的任何值,它都会返回 true。这使得它可以安全地使用跨领域(cross-realm)对象,其中 Array 构造函数的标识是不同的,因此会导致 instanceof Array 失败。
[!TIP]
有关更多细节,请参阅文章“确定 JavaScript 对象是否为数组”。
2.3.2 示例
console.log(Array.isArray([1, 3, 5]));
// Expected output: true
console.log(Array.isArray('[]'));
// Expected output: false
console.log(Array.isArray(new Array(5)));
// Expected output: true
console.log(Array.isArray(new Int16Array([15, 33])));
// Expected output: false
// 下面的函数调用都返回 true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
Array.isArray(new Array("a", "b", "c", "d"));
Array.isArray(new Array(3));
// 鲜为人知的事实:其实 Array.prototype 也是一个数组:
Array.isArray(Array.prototype);
// 下面的函数调用都返回 false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray("Array");
Array.isArray(true);
Array.isArray(false);
Array.isArray(new Uint8Array(32));
// 这不是一个数组,因为它不是使用数组字面量语法或 Array 构造函数创建的
Array.isArray({ __proto__: Array.prototype });
2.3.3 instanceof 和 Array.isArray()
当检测 Array
实例时,Array.isArray
优于 instanceof
,因为 Array.isArray
能跨领域工作。
// 创建一个新的 iframe 元素
const iframe = document.createElement("iframe");
// 将 iframe 添加到当前文档的 body 中
document.body.appendChild(iframe);
// 获取刚刚创建的 iframe 中的 Array 构造函数
// window.frames 是一个类数组对象,包含当前文档中所有的 iframe
// window.frames[window.frames.length - 1] 获取最后一个 iframe,即刚刚创建的那个
const xArray = window.frames[window.frames.length - 1].Array;
// 使用 iframe 中的 Array 构造函数创建一个新的数组实例
const arr = new xArray(1, 2, 3); // 创建一个数组 [1, 2, 3]
// 使用 Array.isArray 方法正确检查 arr 是否为数组
Array.isArray(arr); // true,因为 arr 是一个数组
// 使用 instanceof 检查 arr 是否是主文档中的 Array 的实例
// arr 的原型是 xArray.prototype,而不是主文档中的 Array.prototype
arr instanceof Array; // false,因为 arr 的原型链中没有主文档的 Array.prototype
2.3 Array.of() 🏳️
静态方法通过可变数量的参数创建一个新的 Array
实例,而不考虑参数的数量或类型。
Array.of() 不会根据参数的数量和类型来决定数组的行为,而是简单地将所有参数作为数组的元素。
[!TIP]
利用英文巧记:(These elements) are part of the array 括号里的内容组成了数组
2.3.1 语法
Array.of()
Array.of(element0)
Array.of(element0, element1)
Array.of(element0, element1, /* … ,*/ elementN)
参数
-
elementN
用于创建数组的元素。
返回值
新的 Array
实例。
[!NOTE]
- 通用工厂方法:
Array.of() 可以被任何类继承,不仅限于 Array 本身。
当子类继承 of() 方法时,调用 of() 会返回该子类的新实例,而不是 Array 的实例。
- this 值的灵活性:
Array.of() 的 this 值可以是任何构造函数,只要它能接受一个参数来表示新数组的长度。
当调用 of() 时,构造函数会根据传递给 of() 的参数数量来创建实例。
- 元素分配和长度设置:
Array.of() 会将所有传入的参数作为数组的元素。
在所有元素被分配后,数组的 length 属性会被设置为元素的数量。
- 默认行为:
如果 this 值不是一个构造函数,Array.of() 会使用默认的 Array 构造函数来创建数组。
2.3.2 示例
1. 与Array构造函数的不同
Array.of(7); // [7]
Array(7); // 由 7 个空槽组成的数组
Array.of(1, 2, 3); // [1, 2, 3]
Array(1, 2, 3); // [1, 2, 3]
2.非数组构造函数调用 of()
把this指针绑定到NotArray函数上 返回类数组对象
function NotArray(len) {
console.log("NotArray called with length", len);
}
console.log(Array.of.call(NotArray, 1, 2, 3));
// NotArray called with length 3
// NotArray { '0': 1, '1': 2, '2': 3, length: 3 }
console.log(Array.of.call(Object)); // [Number: 0] { length: 0 }
// 非数组构造函数必须能传入参数
[!NOTE]
如果以上教程有错误描述,欢迎来评论区批或者私信评指正
参考
1.Array - JavaScript | MDN (mozilla.org)
2.5 Handy Applications of JavaScript Array.from() (dmitripavlutin.com)