如何从零开始实现 JavaScript 数组方法

介绍

JavaScript 包含多种功能来处理超出for循环的数组,您可能已经在自己的项目中使用了这些功能,并想知道它们是如何工作的,以及为什么您会使用它们。

在本文中,您将通过创建您自己的版本的地图过滤分类减少从零开始来完成。

通过将 ES6 箭头函数与 JavaScript Array 函数相结合,您可以写出非常强大、清晰的代码。

JavaScript Array 方法如何工作

让我们从一个例子开始吧. 假设您想要重复通过数组,增加每个元素一个,然后返回新的数组. 在过去,您需要做几个事情来实现这一点:

  • 初始化一个新的空数组
  • 重复原始数组中的每个元素
  • 更改该元素并将更改的值放入新数组

代码看起来会是这样的:

1const arr = [1, 2, 3];
2const newArray = [];
3
4for (let i = 0; i < arr.length; i++) {
5    newArray[i] = arr[i] + 1;
6}
7
8return newArray;

但是,有了内置的地图函数,你可以在一行代码中实现这一点:

1return arr.map(element => ++element);

JavaScript Array 方法大大利用 ES6 箭头函数

我们将涵盖的每一个数组函数都将接受一个函数作为参数,它们将通过数组的每个元素重复,并调用该函数来确定每个元素要做什么。

前提条件

要本地遵循本教程,您需要一个编辑器(如 Visual Studio Code)和一个沙盒环境扩展程序(如 Quokka.js)。

若要在線跟隨教程,您可以使用 CodePenCodeSandbox

步骤 1 - 实施地图

地图通过每个元素迭代,以某种方式转换,将其添加到新数组中,并返回新数组。

<$>[警告] 警告:在本文中,您将用自定义功能扩展JavaScript全球对象。

JavaScript 数组函数是 Array 原型的一部分,类似于 Java 类中的函数,例如. 若要将这些函数超级,您可以将一个新的函数分配到 `Array.prototype'。

让我们创建一个新的函数,并将其分配到 Array.prototype.mapFromScratch:

1const myCustomMapFunction = function(callback) {
2    console.log('My Custom Map Function!');
3}
4Array.prototype.mapFromScratch = myCustomMapFunction;
5
6const arr = [1, 2, 3];
7
8arr.mapFromScratch();

如果您运行此代码,您将在控制台中看到日志消息:

1[secondary_label Output]
2My Custom Map Function!

现在,添加for循环并打印每个元素. 由于数组本身调用方法,您可以通过引用this来访问该数组:

1const myCustomMapFunction = function(callback) {
2    console.log('My Custom Map Function!');
3
4    // 'this' refers to the array
5    for (let i = 0; i < this.length; i++) {
6        console.log(this[i]);
7    }
8
9}

现在,通过调用回调函数来执行任何所需的转换,当您这样做时,您将传递当前元素和当前索引:

1const myCustomMapFunction = function(callback) {
2    console.log('My Custom Map Function!');
3
4    // 'this' refers to the array
5    for (let i = 0; i < this.length; i++) {
6        const transformedElement = callback([this[i], i);
7    }
8
9}

最后,将转换的元素添加到新数组中,然后返回该数组。

 1const myCustomMapFunction = function(callback) {
 2    console.log('My Custom Map Function!');
 3
 4    const newArray = [];
 5
 6    // 'this' refers to the array
 7    for (let i = 0; i < this.length; i++) {
 8        newArray[i] = callback(this[i], i);
 9    }
10
11    return newArray;
12}

让我们来看看你的重写函数在行动中,通过测试它与一个函数,将增加每一个值在数组:

1// arr = [1, 2, 3]
2// expected = [2, 3, 4]
3
4console.log(arr.mapFromScratch((element) => ++element));

您将获得以下输出:

1[secondary_label Output]
2My Custom Map Function!
3[2, 3, 4]

在此步骤中,您实现了自定义的地图函数,接下来,让我们探索实现过滤器函数。

步骤 2 - 执行过滤器

过滤函数返回从原始数组中过滤的元素的新数组。

让我们创建一个新的函数,并将其分配到 Array.prototype.filterFromScratch:

1const myCustomFilterFunction = function(callback) {
2    console.log('My Custom Filter Function!');
3}
4Array.prototype.filterFromScratch = myCustomFilterFunction;
5
6const arr = [1, 2, 3];
7
8arr.filterFromScratch();

现在,设置for循环来迭代每个元素:

1const myCustomFilterFunction = function(callback) {
2    console.log('My Custom Filter Function!');
3
4    const newArray = [];
5
6    for (let i = 0; i < this.length; i++) {
7        console.log(this[i]);
8    }
9}

for循环中,你需要决定是否将每个元素添加到新数组中,这是回调函数的目的,所以你可以使用它来条件地添加每个元素,如果返回值是true,请将元素推到返回数组:

 1const myCustomFilterFunction = function(callback) {
 2    console.log('My Custom Filter Function!');
 3
 4    const newArray = [];
 5
 6    for (let i = 0; i < this.length; i++) {
 7        if (callback(this[i])) {
 8            newArray.push(this[i]);
 9        }
10    }
11
12    return newArray;
13}

让我们看看你的重写函数在行动中,通过测试它与一个函数,将显示大于1的值:

1// arr = [1, 2, 3]
2// expected = [2, 3]
3
4console.log(arr.filterFromScratch((element) =>  element > 1));

您将获得以下输出:

1[secondary_label Output]
2My Custom Filter Function!
3[2, 3]

有了它,您已经实现了自定义的过滤器函数,接下来您将使用类型函数。

步骤3 - 执行类型

分类函数返回从原始数组中分类的数组。

让我们创建一个新的函数,并将其分配到 Array.prototype.sortFromScratch:

1const myCustomSortFunction = function(callback) {
2    console.log('My Custom Sort Function!');
3}
4Array.prototype.sortFromScratch = myCustomSortFunction;
5
6const arr = [3, 2, 1];
7
8arr.sortFromScratch();

我们将使用 Bubble Sort为此类型的实现. 以下是我们将采取的方法:

  • 重复重复通过数组中的项目
  • 比较邻近的项目,如果它们不按顺序进行交换
  • 经过数组重复足够的时间来进行每个比较后,数组进行排序

使用 Bubble Sort,您必须在数组中的每个元素中完全迭代一次,这需要一个嵌入的for循环,其中内部循环通过停止最后元素的一个短路进行迭代,所以让我们现在添加它。

<$>[注] :这是用于教育目的,并且不是一个高效的排序方法。

 1const myCustomSortFunction = function(callback) {
 2    console.log('My Custom Sort Function!');
 3
 4    const newArray = [];
 5
 6    for (let i = 0; i < newArray.length; i++) {
 7        for (let j = 0; j < newArray.length - 1; j++) { 
 8        }
 9    }
10}

要避免这种情况,您可以使用 Spread Operator将原始数组复制到新数组。

 1const myCustomSortFunction = function(callback) {
 2    console.log('My Custom Sort Function!');
 3
 4    const newArray = [...this];
 5
 6    for (let i = 0; i < newArray.length; i++) {
 7        for (let j = 0; j < newArray.length - 1; j++) { 
 8        }
 9    }
10}

回调函数采取两个参数,当前元素和下一个元素,并将返回它们是否顺序. 在我们的情况下,如果回调函数返回一个大于0的数字,我们想要交换两个元素。

 1const myCustomSortFunction = function(callback) {
 2    console.log('My Custom Sort Function!');
 3
 4    const newArray = [...this];
 5
 6    for (let i = 0; i < newArray.length; i++) {
 7        for (let j = 0; j < newArray.length - 1; j++) {
 8            if (callback(newArray[j], newArray[j + 1]) > 0) {
 9                // swap the elements
10            }
11        }
12    }
13}

要交换元素,您可以创建一个副本,更换第一个副本,然后用副本替换第二个副本。

 1const myCustomSortFunction = function(callback) {
 2    console.log('My Custom Sort Function!');
 3
 4    const newArray = [...this]; 
 5
 6    for (let i = 0; i < newArray.length; i++){
 7        for (let j = 0; j < newArray.length - 1; j++) {
 8            if (callback(newArray[j], newArray[j + 1]) > 0) {
 9                const temp = newArray[j + 1];
10                newArray[j + 1] = newArray[j];
11                newArray[j] = temp;
12            }
13       }
14    }
15
16    // array is sorted
17    return newArray;
18}

让我们看看你的重写函数在行动中,通过测试它用一个从低到高分类的函数:

1// arr = [3, 2, 1]
2// expected = [1, 2, 3]
3
4console.log(arr.sortFromScratch((current, next) => current - next));

您将获得以下输出:

1[secondary_label Output]
2My Custom Sort Function!
3[1, 2, 3]

现在你已经创建了一个自定义的分类函数,你已经准备好继续实施一个减少函数。

步骤4 - 实施减少

减少函数通过每个元素重复并返回一个单个值。

「減少」並不像其他函數一樣返回一個新的數列. 它實際上「減少」數列中的元素到一個最終值:一個數字、一個字符串或一個對象。

让我们创建一个新的函数,并将其分配到Array.prototype.reduceFromScratch:

1const myCustomReduceFunction = function(callback) {
2    console.log('My Custom Reduce Function!');
3}
4Array.prototype.reduceFromScratch = myCustomReduceFunction;
5
6const arr = [1, 2, 3];
7
8arr.reduceFromScratch();

减少返回一个最终值,它需要一个起始值来工作。用户通过的回调函数将决定如何根据数组的每个元素更新此 accumulator 并在末尾返回它。

现在添加您的for循环,并拨打回调函数. 返回值成为新积累器. 循环结束后,您返回积累器。

1const myCustomReduceFunction = function(callback, accumulator) {
2    console.log('My Custom Reduce Function!');
3    for (let i = 0; i < this.length; i++) {
4        accumulator = callback(accumulator, this[i]);
5    }
6    return accumulator;
7}

让我们看看你的重写函数在行动中,通过测试它与一个函数,总结一个数组的内容:

1// arr = [1, 2, 3]
2// expected = 6
3
4console.log(arr.reduceFromScratch((accumulator, element) => accumulator + element, 0));
1[secondary_label Output]
2My Custom Reduce Function!
36

结论

JavaScript 的数组功能非常有用. 在本教程中,您重新实现了数组功能,以便更好地了解它们的工作方式,以便您可以更有效地使用它们。

Published At
Categories with 技术
Tagged with
comments powered by Disqus