- findLast()和findLastIndex()
- Hashbang语法
- Symbol作为WeakMap的key
- 复制改变数组
findLast()和findLastIndex()
Array.prototype.findLast()
findLast() 方法反向迭代数组,并返回满足提供的测试函数的第一个匹配的元素。如果没有找到对应元素,则返回 undefined。
const array1 = [5, 12, 50, 130, 44];
const found = array1.findLast((element) => element > 45);
console.log(found);
// Expected output: 130
语法
findLast(callbackFn)
findLast(callbackFn, thisArg)
参数
callbackFn
: 数组中测试元素的函数,找到匹配的元素会返回true,否则返回false,函数在被调用时会传递以下参数:
element
: 当前遍历到的元素。
index
:当前遍历元素的索引。
array
:调用findLast()的数组本身。
thisArg
:执行callbackFn时的this。
返回值
满足提供的测试函数的第一个匹配的元素。
描述
findLast() 是一个迭代方法。该方法对数组从后到前执行 callbackFn 函数,直到 callbackFn 返回true。然后 findLast() 返回该元素并停止遍历数组。如果 callbackFn 遍历完数组也没找到匹配元素,则 findLast() 返回 undefined。
callbackFn 会被数组中的每个元素调用,而不仅仅是那些被赋值的元素。对于稀疏数组来说(含有empty),空槽行为和 undefined 相同。
findLast()
方法不会改变调用它的数组,但是提供的 callbackFn
可以。但是请注意,数组的长度是在第一次调用 callbackFn
之前保存的。因此:
callbackFn
不会访问在调用findLast()
开始后才添加到数组中的任何元素。- 给已访问过的索引重新赋值将不会被
callbackFn
重新访问。 - 如果
callbackFn
更改了数组中现有的、尚未访问的元素,则其传递给callbackFn
的值将是findLast()
访问该元素索引时的值。已删除的元素会被当做undefined
来访问。
注意: 以上描述的并发修改的情况经常导致难以理解的代码,通常应该避免(特殊情况除外)。
示例
查找与元素属性匹配的数组中的最后一个对象
const inventory = [
{ name: "apples", quantity: 2 },
{ name: "bananas", quantity: 0 },
{ name: "fish", quantity: 1 },
{ name: "cherries", quantity: 5 },
];
// 库存低时返回 true
function isNotEnough(item) {
return item.quantity < 2;
}
console.log(inventory.findLast(isNotEnough));
// { name: "fish", quantity: 1 }
使用箭头函数和解构
const inventory = [
{ name: "apples", quantity: 2 },
{ name: "bananas", quantity: 0 },
{ name: "fish", quantity: 1 },
{ name: "cherries", quantity: 5 },
];
const result = inventory.findLast( ({ quantity }) => quantity < 2 );
console.log(result)
// { name: "fish", quantity: 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, 12].findLast(isPrime)); // undefined,没有找到
console.log([4, 5, 7, 8, 9, 11, 12].findLast(isPrime)); // 11
在稀疏数组上使用 findLast()
稀疏数组中的空槽被访问,并被视为 undefined
。
// 声明一个在索引 2、3 和 4 处没有元素的数组
const array = [0, 1, , , , 5, 6];
// 显示所有的索引(不只包括那些被赋值的)
array.findLast((value, index) => {
console.log(`访问索引 ${index},值为 ${value}`);
});
// 访问索引 6,值为 6
// 访问索引 5,值为 5
// 访问索引 4,值为 undefined
// 访问索引 3,值为 undefined
// 访问索引 2,值为 undefined
// 访问索引 1,值为 1
// 访问索引 0,值为 0
// 显示所有的索引(包括已被删除的)
array.findLast((value, index) => {
// 在第一次迭代时删除值为 5 的元素
if (index === 6) {
console.log(`删除值为 array[5],其值为 ${array[5]}`);
delete array[5];
}
// 元素 5 在被删除后,仍会被访问
console.log(`访问索引 ${index},值为 ${value}`);
});
// 删除值为 array[5],其值为 5
// 访问索引 6,值为 6
// 访问索引 5,值为 undefined
// 访问索引 4,值为 undefined
// 访问索引 3,值为 undefined
// 访问索引 2,值为 undefined
// 访问索引 1,值为 1
// 访问索引 0,值为 0
在非数组对象上调用 findLast()
const arrayLike = {
length: 3,
0: 2,
1: 7.3,
2: 4,
};
console.log(
Array.prototype.findLast.call(arrayLike, (x) => Number.isInteger(x)),
); // 4
Array.prototype.findLastIndex()
findLastIndex()
方法反向迭代数组,并返回满足所提供的测试函数的第一个元素的索引。若没有找到对应元素,则返回 -1。
const array1 = [5, 12, 50, 130, 44];
const isLargeNumber = (element) => element > 45;
console.log(array1.findLastIndex(isLargeNumber));
// Expected output: 3
// Index of element with value: 130
语法
findLastIndex(callbackFn)
findLastIndex(callbackFn, thisArg)
参数
callbackFn
:对数组中的每个元素执行的函数。若找到匹配的元素,回调返回true,否则返回false。函数在被调用时会传递以下参数:
element
:当前遍历到的元素。
index
:当前正在处理的元素的索引。
array
:调用 findLastIndex()
的数组本身。
thisArg
:执行 callbackFn
时的this。
返回值
数组中通过测试的最后一个(索引最大)元素的索引。如果没有找到任何匹配的元素,则返回 -1。
描述
findLastIndex() 方法是一个迭代方法。它为数组倒序调用提供的 callbackFn 函数,直到 callbackFn 返回true。然后 findLastIndex() 返回元素的索引并且停止遍历数组。如果遍历完数组 callbackFn 也没有返回true,则 findLastIndex() 返回 -1。
示例
在稀疏数组上使用 findLastIndex()
console.log([1, , 3].findLastIndex((x) => x === undefined)); // 1
Hashbang 注释
Hashbang 注释是一种特殊的注释语法,其行为与单行注释(//)完全一样,只是它以 #! 开头,并且只在脚本或模块的最开始处有效。注意,#! 标志之前不能有任何空白字符。注释由 #! 之后的所有字符组成直到第一行的末尾;只允许有一条这样的注释。JavaScript 中的 hashbang 注释类似于 Unix 中的 shebang,它提供了一个特定的 JavaScript 解释器的路径,你想用它来执行这个脚本。在 hashbang 注释标准化之前,它已经在非浏览器主机(如 Node.js)中得到了事实上的实现,在那里,它在被传递给引擎之前被从源文本中剥离。示例如下:
#!/usr/bin/env node
console.log("Hello world");
JavaScript 的解释器会把它视为普通注释——只有当脚本直接在 shell 中运行时,它对 shell 才有语义意义。
你只能使用 #!
注释样式以指定 JavaScript 解释器。在所有其他情况下,只需使用 //
注释(或多行注释)。
Symbol作为WeakMap的key
在 JavaScript 中,Object和Symbol是保证唯一并且不能重新创建的,这使得它们都是 WeakMap 键的绝佳选择。 以前的版本或规范只允许使用Object,但幸运的是最新提出的将Symbol作为 WeakMap 键的提案将非注册Symbol添加到允许的键列表中。
const weak = new WeakMap();
const key = Symbol("ref");
weak.set(key, "ECMAScript 2023");
console.log(weak.get(key));
// ECMAScript 2023
通过复制改变数组
Array.prototype上的reverse()、sort()和splice()会改变原数组。于是新提案提出了toReversed()、toSorted()、toSpliced()和with()。
Array.prototype.toReversed()
reverse() 方法对应的复制版本。它返回一个包含以相反顺序排列元素的新数组。
示例
反转数组中的元素
const items = [1, 2, 3];
console.log(items); // [1, 2, 3]
const reversedItems = items.toReversed();
console.log(reversedItems); // [3, 2, 1]
console.log(items); // [1, 2, 3]
在稀疏数组上使用 toReversed()
console.log([1, , 3].toReversed()); // [3, undefined, 1]
console.log([1, , 3, 4].toReversed()); // [4, 3, undefined, 1]
在非数组对象上调用 toReversed()
const arrayLike = {
length: 3,
unrelated: "foo",
2: 4,
};
console.log(Array.prototype.toReversed.call(arrayLike));
// [4, undefined, undefined]
// '0' 和 '1' 两个索引不存在,所以它们会变成 undefined
Array.prototype.toSorted()
sort() 方法的复制方法版本。它返回一个新数组,其元素按升序排列。
// 不传入函数
toSorted()
// 传入箭头函数
toSorted((a, b) => { /* … */ })
// 传入比较函数
toSorted(compareFn)
// 內联比较函数
toSorted(function compareFn(a, b) { /* … */ })
参数
compareFn
:指定一个定义排序顺序的函数。如果省略,则将数组元素转换为字符串,然后根据每个字符的 Unicode 码位值进行排序。
a
:用于比较的第一个元素。
b
:用于比较的第二个元素。
示例
对数组进行排序
const months = ["Mar", "Jan", "Feb", "Dec"];
const sortedMonths = months.toSorted();
console.log(sortedMonths); // ['Dec', 'Feb', 'Jan', 'Mar']
console.log(months); // ['Mar', 'Jan', 'Feb', 'Dec']
const values = [1, 10, 21, 2];
const sortedValues = values.toSorted((a, b) => a - b));
console.log(sortedValues); // [1, 2, 10, 21]
console.log(values); // [1, 10, 21, 2]
在稀疏数组上使用 toSorted()
空槽被视为具有 undefined
值而被排序。它们总是排序到数组的末尾,并且 compareFn
不会对它们进行调用。
console.log(["a", "c", , "b"].toSorted()); // ['a', 'b', 'c', undefined]
console.log([, undefined, "a", "b"].toSorted()); // ["a", "b", undefined, undefined]
在非数组对象上调用 toSorted()
toSorted()
方法会读取 this
的 length
属性。然后它会收集所有在 0
到 length - 1
范围内的整数键属性,对它们进行排序并将它们写入一个新的数组中。
const arrayLike = {
length: 3,
unrelated: "foo",
0: 5,
2: 4,
};
console.log(Array.prototype.toSorted.call(arrayLike));
// [4, 5, undefined]
Array.prototype.toSpliced()
splice() 方法的复制版本。它返回一个新数组,并在给定的索引处删除和/或替换了一些元素。
toSpliced(start)
toSpliced(start, deleteCount)
toSpliced(start, deleteCount, item1)
toSpliced(start, deleteCount, item1, item2, itemN)
参数
start
:从 0 开始计算的索引,表示要开始改变数组的位置,它会被转换为整数。
- 如果
start < 0
,则从数组末尾开始计数,使用start + array.length
。 - 如果
start < -array.length
或者省略了start
,则使用0
。 - 如果
start >= array.length
,不会删除任何元素,但该方法将表现为添加元素的函数,添加提供的所有元素
deleteCount
:可选,一个整数,指示数组中要从 start
删除的元素数量。如果 deleteCount 被省略了,或者如果它的值大于或等于由 start 指定的位置到数组末尾的元素数量,将会删除从 start 到数组末尾的所有元素。但是,如果你想要传递任何 itemN 参数,则应向 deleteCount 传递 Infinity 值,以删除 start 之后的所有元素,因为显式的 undefined 会转换为 0。
如果 deleteCount
是 0
或者负数,则不会删除元素。在这种情况下,你应该指定至少一个新元素(见下文)。
item1
, …, itemN
:可选,元素将从 start
开始添加到数组当中。如果你没有指定任何元素,toSpliced()
只会从数组中删除元素。
示例
删除、添加和替换元素
比使用 slice()
和 concat()
更高效。
const months = ["Jan", "Mar", "Apr", "May"];
// 在索引 1 处添加一个元素
const months2 = months.toSpliced(1, 0, "Feb");
console.log(months2); // ["Jan", "Feb", "Mar", "Apr", "May"]
// 从第 2 个索引开始删除两个元素
const months3 = months2.toSpliced(2, 2);
console.log(months3); // ["Jan", "Feb", "May"]
// 在索引 1 处用两个新元素替换一个元素
const months4 = months3.toSpliced(1, 1, "Feb", "Mar");
console.log(months4); // ["Jan", "Feb", "Mar", "May"]
// 原数组不会被修改
console.log(months); // ["Jan", "Mar", "Apr", "May"]
Array.prototype.with()
使用方括号表示法修改指定索引值的复制方法版本。它会返回一个新数组,其指定索引处的值会被新值替换。原数组不会被修改。
array.with(index, value)
参数
index
:要修改的数组索引(从 0 开始),将会转换为整数。
- 负数索引会从数组末尾开始计数——即当
index < 0
时,会使用index + array.length
。 - 如果规范化后的索引超出数组边界,会抛出 RangeError。
value
:要分配给指定索引的任何值。
示例
创建一个新的数组,改变其中一个元素
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]
数组方法的链式调用
在更新一个数组元素后继续调用其他的数组方法。
const arr = [1, 2, 3, 4, 5];
console.log(arr.with(2, 6).map((x) => x ** 2)); // [1, 4, 36, 16, 25]