11.前端动态-ES2023将新增的数组方法-《前端知识进阶》

admin 2025-11-01 16:01:04 编程 来源:ZONE.CI 全球网 0 阅读模式
  • 1. 通过副本更改数组
    • (1)Array.prototype.toReversed()
    • (2)Array.prototype.toSorted()
    • (3)Array.prototype.toSpliced()
    • (4)Array.prototype.with()
  • 2. 数组分组
    • (1)概述
    • (2)使用
    • (3)polyfill
  • 3. 从尾到头搜索数组
    • (1)概述
    • (3)polyfill
    • (4)参考源码
  • 4. Array.fromAsync

    ECMAScript 规范每年都会更新一次,正式标准化 JavaScript 语言的 ECMAScript 的下一次年度更新将在 2023 年 6 月左右获得批准,这将是 ECMAScript 的第 14 版。所有在 2023 年 3 月之前达到阶段 4 的提案都将包含在 ECMAScript 2023 标准中。 对于一个提案,从提出到最后被纳入ECMAScript 标准,TC39的规范中分为五步:

    • stage0(strawman):任何TC39的成员都可以提交。
    • stage1(proposal):进入此阶段就意味着这一提案被认为是正式的了,需要对此提案的场景与API进行详尽的描述。
    • stage2(draft):演进到这一阶段的提案如果能最终进入到标准,那么在之后的阶段都不会有太大的变化,因为理论上只接受增量修改。
    • state3(candidate):这一阶段的提案只有在遇到了重大问题才会修改,规范文档需要被全面的完成。
    • state4(finished):这一阶段的提案将会被纳入到ES每年发布的规范之中。

    、根据 Erick Wendel(微软 MVP、谷歌开发专家、@nodejs合作者)的预测,ECMAScript 2023 可能会新增以下数组方法:

    • 3️⃣ Array.prototype.toReversed()
    • 3️⃣ Array.prototype.toSorted()
    • 3️⃣ Array.prototype.toSpliced()
    • 3️⃣ Array.prototype.with()
    • 3️⃣ Array.prototype.group()
    • 3️⃣ Array.prototype.groupToMap()
    • 4️⃣ Array.prototype.findLast()
    • 4️⃣ Array.prototype.findLastIndex()
    • 3️⃣ Array.fromAsync()

    下面就来看看这些方法是如何使用的吧!

    1. 通过副本更改数组

    通过副本更改数组的提案目前处于第 3 阶段。该提案为数组和类型化数组提出了四种新的方法:

    • Array.prototype.toReversed()
    • Array.prototype.toSorted()
    • Array.prototype.toSpliced()
    • Array.prototype.with()

      提案地址:https://github.com/tc39/proposal-change-array-by-copy

    为什么会有这个提案呢?我们知道,大多数的数组方法都是非破坏性的,也就是说,在数组执行该方法时,不会改变原数组,比如 filter() 方法:

    1. const arr = ['a', 'b', 'b', 'a'];
    2. const result = arr.filter(x => x !== 'b');
    3. console.log(result); // ['a', 'a']

    当然,也有一些是破坏性的方法,它们在执行时会改变原数组,比如 sort() 方法:

    1. const arr = ['c', 'a', 'b'];
    2. const result = arr.sort();
    3. console.log(result); // ['a', 'b', 'c']

    在数组的方法中,下面的方法是具有破坏性的:

    • .reverse()
    • .sort()
    • .splice()

    如果我们想要这些数组方法应用于数组而不改变它,可以使用下面任意一种形式:

    1. const sorted1 = arr.slice().sort();
    2. const sorted2 = [...arr].sort();
    3. const sorted3 = Array.from(arr).sort();

    可以看到,我们首先需要创建数组的副本,再对这个副本进行修改。

    因此改提案就引入了这三个方法的非破坏性版本,因此不需要手动创建副本再进行操作:

    • .reverse() 的非破坏性版本:.toReversed()
    • .sort() 非破坏性版本:.toSorted(compareFn)
    • .splice() 非破坏性版本:.toSpliced(start, deleteCount, ...items)

    该提案将这些函数属性引入到 Array.prototype

    • Array.prototype.toReversed() -> Array
    • Array.prototype.toSorted(compareFn) -> Array
    • Array.prototype.toSpliced(start, deleteCount, …items) -> Array
    • Array.prototype.with(index, value) -> Array

    除此之外,该提案还还提出了一个新的非破坏性方法:with()。该方法会以非破坏性的方式替换给定 index 处的数组元素,即 arr[index]=value 的非破坏性版本。

    所有这些方法都将保持目标数组不变,并返回它的副本并执行更改。这些方法适用于数组,也适用于类型化数组,即以下类的实例:

    • Int8Array
    • Uint8Array
    • Uint8ClampedArray
    • Int16Array
    • Uint16Array
    • Int32Array
    • Uint32Array
    • Float32Array
    • Float64Array
    • BigInt64Array
    • BigUint64Array

    TypedArray是一种通用的固定长度缓冲区类型,允许读取缓冲区中的二进制数据。其在WEBGL规范中被引入用于解决Javascript处理二进制数据的问题。类型化数组也是数组,只不过其元素被设置为特定类型的值。

    类型化数组的核心就是一个名为 ArrayBuffer 的类型。每个ArrayBuffer对象表示的只是内存中指定的字节数,但不会指定这些字节用于保存什么类型的数据。通过ArrayBuffer能做的就是为了将来使用而分配一定数量的字节。

    这些提案也适用于元组,元组相当于不可变的数组。它们拥有数组的所有方法——除了破坏性的方法。因此,将后者的非破坏性版本添加到数组对元组是有帮助的,这意味着我们可以使用相同的方法来非破坏性地更改数组和元组。

    (1)Array.prototype.toReversed()

    .toReversed().reverse() 方法的非破坏性版本:

    1. const arr = ['a', 'b', 'c'];
    2. const result = arr.toReversed();
    3. console.log(result); // ['c', 'b', 'a']
    4. console.log(arr); // ['a', 'b', 'c']

    下面是 .toReversed() 方法的一个简单的 polyfill:

    1. if (!Array.prototype.toReversed) {
    2. Array.prototype.toReversed = function () {
    3. return this.slice().reverse();
    4. };
    5. }

    (2)Array.prototype.toSorted()

    .toSorted().sort() 方法的非破坏性版本:

    1. const arr = ['c', 'a', 'b'];
    2. const result = arr.toSorted();
    3. console.log(result); // ['a', 'b', 'c']
    4. console.log(arr); // ['c', 'a', 'b']

    下面是 .toSorted() 方法的一个简单的 polyfill:

    1. if (!Array.prototype.toSorted) {
    2. Array.prototype.toSorted = function (compareFn) {
    3. return this.slice().sort(compareFn);
    4. };
    5. }

    (3)Array.prototype.toSpliced()

    .splice() 方法比其他几种方法都复杂,其使用形式:splice(start, deleteCount, ...items)。该方法会从从 start 索引处开始删除 deleteCount个元素,然后在 start 索引处开始插入item 中的元素,最后返回已经删除的元素。

    .toSpliced.splice() 方法的非破坏性版本,它会返回更新后的数组,原数组不会变化,并且我们无法再得到已经删除的元素:

    1. const arr = ['a', 'b', 'c', 'd'];
    2. const result = arr.toSpliced(1, 2, 'X');
    3. console.log(result); // ['a', 'X', 'd']
    4. console.log(arr); // ['a', 'b', 'c', 'd']

    下面是 .toSpliced() 方法的一个简单的 polyfill:

    1. if (!Array.prototype.toSpliced) {
    2. Array.prototype.toSpliced = function (start, deleteCount, ...items) {
    3. const copy = this.slice();
    4. copy.splice(start, deleteCount, ...items);
    5. return copy;
    6. };
    7. }

    (4)Array.prototype.with()

    .with()方法的使用形式:.with(index, value),它是 arr[index] = value 的非破坏性版本。

    1. const arr = ['a', 'b', 'c'];
    2. const result = arr.with(1, 'X');
    3. console.log(result); // ['a', 'X', 'c']
    4. console.log(arr); // ['a', 'b', 'c']

    下面是 .with() 方法的一个简单的 polyfill:

    1. if (!Array.prototype.with) {
    2. Array.prototype.with = function (index, value) {
    3. const copy = this.slice();
    4. copy[index] = value;
    5. return copy;
    6. };
    7. }

    2. 数组分组

    (1)概述

    在日常开发中,数组分组是一种极其常见的操作。因此,proposal-array-grouping 提案就提出了两个新的数组方法:

    • array.group(callback, thisArg?)
    • array.groupToMap(callback, thisArg?)

      提案地址:https://github.com/tc39/proposal-array-grouping

    下面是这两个方法的类型签名:

    1. Array<Elem>.prototype.group<GroupKey extends (string|symbol)>(
    2. callback: (value: Elem, index: number, array: Array<Elem>) => GroupKey,
    3. thisArg?: any
    4. ): {[k: GroupKey]: Array<Elem>}
    5. Array<Elem>.prototype.groupToMap<GroupKey>(
    6. callback: (value: Elem, index: number, array: Array<Elem>) => GroupKey,
    7. thisArg?: any
    8. ): Map<GroupKey, Array<Elem>>

    这两个方法都用来对数组进行分组:

    • 输入:一个数组;
    • 输出:组,每个组都有一个组key,以及一个包含组成员的数组。

    这两个方法都会对数组进行遍历,它们会向其回调请求组键并将元素添加到相应的组中。这两个方法在表示组的方式上有所不同:

    • group():将组存储在对象中:组键存储为属性键,组成员存储为属性值;
    • groupToMap():将组存储在 Map 中:组键存储为 Map 键,组成员存储为 Map 值。

    那这两个方法该如何选择呢?我们知道,JavaScript 中对象是支持解构的,如果想要使用解构来获取数组中的值,比如,对于上面对象,可以通过解构获取三个不同组的值:

    1. const {vegetables, fruit, meat} = result;

    而 Map 的好处就是它的 key 不限于字符串和symbol,更加自由。

    (2)使用

    下面来看几个实用例子。假如执行 Promise.allSettled() 方法返回的数组如下:

    1. const settled = [
    2. { status: 'rejected', reason: 'Jhon' },
    3. { status: 'fulfilled', value: 'Jane' },
    4. { status: 'fulfilled', value: 'John' },
    5. { status: 'rejected', reason: 'Jaen' },
    6. { status: 'rejected', reason: 'Jnoh' },
    7. ];
    8. const {fulfilled, rejected} = settled.group(x => x.status);
    9. // fulfilled 结果如下:
    10. [
    11. { status: 'fulfilled', value: 'Jane' },
    12. { status: 'fulfilled', value: 'John' },
    13. ]
    14. // rejected 结果如下:
    15. [
    16. { status: 'rejected', reason: 'Jhon' },
    17. { status: 'rejected', reason: 'Jaen' },
    18. { status: 'rejected', reason: 'Jnoh' },
    19. ]

    在这个例子中,使用 group() 的效果会更好,因为可以使用解构轻松获取需要组的值。

    假如想要对以下数组中人根据国家进行分组:

    1. const persons = [
    2. { name: 'Louise', country: 'France' },
    3. { name: 'Felix', country: 'Germany' },
    4. { name: 'Ava', country: 'USA' },
    5. { name: 'Léo', country: 'France' },
    6. { name: 'Oliver', country: 'USA' },
    7. { name: 'Leni', country: 'Germany' },
    8. ];
    9. const result = persons.groupToMap((person) => person.country);
    10. // result 的执行结果和以下 Map 是等价的:
    11. new Map([
    12. [
    13. 'France',
    14. [
    15. { name: 'Louise', country: 'France' },
    16. { name: 'Léo', country: 'France' },
    17. ]
    18. ],
    19. [
    20. 'Germany',
    21. [
    22. { name: 'Felix', country: 'Germany' },
    23. { name: 'Leni', country: 'Germany' },
    24. ]
    25. ],
    26. [
    27. 'USA',
    28. [
    29. { name: 'Ava', country: 'USA' },
    30. { name: 'Oliver', country: 'USA' },
    31. ]
    32. ],
    33. ])

    在这个例子中,groupToMap() 是更好的选择,因为哦嗯嗯可以在Map 中使用任何类型的键,而在对象中,键值只能是字符串或symbol

    (3)polyfill

    下面来实现一下这两个方法:

    • Array.prototype.group

      1. Array.prototype.group = function (callback, thisArg) {
      2. const result = Object.create(null);
      3. for (const [index, elem] of this.entries()) {
      4. const groupKey = callback.call(thisArg, elem, index, this);
      5. if (! (groupKey in result)) {
      6. result[groupKey] = [];
      7. }
      8. result[groupKey].push(elem);
      9. }
      10. return result;
      11. };
    • Array.prototype.groupToMap

      1. Array.prototype.groupToMap = function (callback, thisArg) {
      2. const result = new Map();
      3. for (const [index, elem] of this.entries()) {
      4. const groupKey = callback.call(thisArg, elem, index, this);
      5. let group = result.get(groupKey);
      6. if (group === undefined) {
      7. group = [];
      8. result.set(groupKey, group);
      9. }
      10. group.push(elem);
      11. }
      12. return result;
      13. };

      3. 从尾到头搜索数组

      (1)概述

      在 JavaScript 中,通过 find()findIndex() 查找数组中的值是一种常见做法。不过,这些方法从数组的开始进行遍历: ```javascript const array = [{v: 1}, {v: 2}, {v: 3}, {v: 4}, {v: 5}];

    array.find(elem => elem.v > 3); // {v: 4} array.findIndex(elem => elem.v > 3); // 3

    1. 如果要从数组的末尾开始遍历,就必须反转数组并使用上述方法。这样做就需要一个额外的数组操作。幸运的是,Wenlu Wang 和 Daniel Rosenwasser 关于`findLast()` 和 `findLastIndex()` 的 ECMAScript 提案解决了这一问题。该提案的一个重要原因就是:**语义**。
    2. > 提案地址:[https://github.com/tc39/proposal-array-find-from-last](https://github.com/tc39/proposal-array-find-from-last)
    3. <a name="gntM6"></a>
    4. ### (2)使用
    5. 它们的用法和`find()`、`findIndex()`类似,唯一不同的是它们是 **从后向前 **遍历数组,这两个方法适用于**数组**和**类数组**。
    6. - `findLast()` 会返回第一个查找到的元素,如果没有找到,就会返回 `undefined`;
    7. - `findLastIndex()` 会返回第一个查找到的元素的索引。如果没有找到,就会返回 -1;
    8. ```javascript
    9. const array = [{v: 1}, {v: 2}, {v: 3}, {v: 4}, {v: 5}];
    10. array.findLast(elem => elem.v > 3); // {v: 5}
    11. array.findLastIndex(elem => elem.v > 3); // 4
    12. array.findLastIndex(elem => elem.v > 5); // undefined

    (3)polyfill

    下面来实现一下这两个方法:

    • Array.prototype.findLast

      1. Array.prototype.findLast = function(arr, callback, thisArg) {
      2. for (let index = arr.length - 1; index >= 0; index--) {
      3. const value = arr[index];
      4. if (callback.call(thisArg, value, index, arr)) {
      5. return value;
      6. }
      7. }
      8. return undefined;
      9. }
    • Array.prototype.findLastIndex

      1. Array.prototype.findLastIndex = function(arr, callback, thisArg) {
      2. for (let index = arr.length - 1; index >= 0; index--) {
      3. const value = arr[index];
      4. if (callback.call(thisArg, value, index, arr)) {
      5. return index;
      6. }
      7. }
      8. return -1;
      9. }

      (4)参考源码

      lodash 中也提供了类似方法,下面是相关源码:

    • findLast() ```javascript import findLastIndex from ‘./findLastIndex.js’ import isArrayLike from ‘./isArrayLike.js’

    /**

    • This method is like find except that it iterates over elements of
    • collection from right to left. *
    • @since 2.0.0
    • @category Collection
    • @param {Array|Object} collection The collection to inspect.
    • @param {Function} predicate The function invoked per iteration.
    • @param {number} [fromIndex=collection.length-1] The index to search from.
    • @returns {*} Returns the matched element, else undefined.
    • @see find, findIndex, findKey, findLastIndex, findLastKey
    • @example *
    • findLast([1, 2, 3, 4], n => n % 2 == 1)
    • // => 3 */ function findLast(collection, predicate, fromIndex) { let iteratee const iterable = Object(collection) if (!isArrayLike(collection)) { collection = Object.keys(collection) iteratee = predicate predicate = (key) => iteratee(iterable[key], key, iterable) } const index = findLastIndex(collection, predicate, fromIndex) return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined }

    export default findLast

    1. - **findLastIndex()**
    2. ```javascript
    3. import baseFindIndex from './.internal/baseFindIndex.js'
    4. import toInteger from './toInteger.js'
    5. /**
    6. * This method is like `findIndex` except that it iterates over elements
    7. * of `collection` from right to left.
    8. *
    9. * @since 2.0.0
    10. * @category Array
    11. * @param {Array} array The array to inspect.
    12. * @param {Function} predicate The function invoked per iteration.
    13. * @param {number} [fromIndex=array.length-1] The index to search from.
    14. * @returns {number} Returns the index of the found element, else `-1`.
    15. * @see find, findIndex, findKey, findLast, findLastKey
    16. * @example
    17. *
    18. * const users = [
    19. * { 'user': 'barney', 'active': true },
    20. * { 'user': 'fred', 'active': false },
    21. * { 'user': 'pebbles', 'active': false }
    22. * ]
    23. *
    24. * findLastIndex(users, ({ user }) => user == 'pebbles')
    25. * // => 2
    26. */
    27. function findLastIndex(array, predicate, fromIndex) {
    28. const length = array == null ? 0 : array.length
    29. if (!length) {
    30. return -1
    31. }
    32. let index = length - 1
    33. if (fromIndex !== undefined) {
    34. index = toInteger(fromIndex)
    35. index = fromIndex < 0
    36. ? Math.max(length + index, 0)
    37. : Math.min(index, length - 1)
    38. }
    39. return baseFindIndex(array, predicate, index, true)
    40. }
    41. export default findLastIndex

    4. Array.fromAsync

    在 JavaScript 中内置了 Array.from 方法,它用于将类数组或者可迭代对象生成一个新的数组实例。在ECMAScript 2018中引入了异步可迭代对象。而JavaScript中一直缺少直接从异步可迭代对象生成数组的内置方法。

    proposal-array-from-async 提案中提出来的 Array.fromAsync 方法就是为了解决这个问题而提出来的。

    下面来看一个简单的例子:

    1. async function * asyncGen (n) {
    2. for (let i = 0; i < n; i++)
    3. yield i * 2;
    4. }
    5. // arr 将变为 [0, 2, 4, 6]`
    6. const arr = [];
    7. for await (const v of asyncGen(4)) {
    8. arr.push(v);
    9. }
    10. // 与上述方式是等价的
    11. const arr = await Array.fromAsync(asyncGen(4));

    Array.fromAsync 可以将异步迭代转换为 promise,并将解析为新数组。在 promise 解析之前,它将从输入值中创建一个异步迭代器,进行惰性的迭代,并将每个产生的值添加到新数组中。

    与其他基于 Promise 的 API 一样,Array.fromAsync 总是会立即返回一个 promise。当 Array.fromAsync 的输入在创建其异步或同步迭代器时引发错误时,则此 promise 状态将被置为 rejected

    提案地址:https://github.com/tc39/proposal-array-from-async

    以太坊cppgolang区别 编程

    以太坊cppgolang区别

    以太坊是一种去中心化的开源平台,它采用智能合约技术,旨在构建和运行不受干扰的分布式应用程序。作为目前最受欢迎的区块链平台之一,以太坊提供了多种编程语言的支持,其
    progolang 编程

    progolang

    Go语言(Golang)是由Google开发的一门静态类型编程语言。作为一名专业的Golang开发者,我深知这门语言的优势和特点。在本文中,我将介绍Golang
    golangn个发送者 编程

    golangn个发送者

    Golang是一种开源的编程语言,由Google团队开发,旨在提高程序的并发性和简化软件开发过程。在Go语言中,有时需要向多个接收者发送信息。本文将介绍如何在G
    golang技能图谱 编程

    golang技能图谱

    从互联网行业的快速发展到人工智能技术的日益成熟,各种编程语言也应运而生。而在这众多的编程语言中,Golang(即Go)作为一门强大且高效的开发语言备受关注。Go
    评论:0   参与:  7