从头开始实现 JavaScript 数组方法

2025-06-07

从头开始实现 JavaScript 数组方法

目录

  1. 介绍
  2. 原型
  3. 数组方法
  4. 资源

介绍

JavaScript Array 类一个全局对象,用于构造数组。Array 是一种特殊类型的可变对象,用于存储多个值。

在本文中,我们将从头开始实现我们自己的数组方法。这些实现并非旨在取代现有的方法,而是为了更好地理解这些方法的工作原理及其用途。

方法 描述
索引() 返回数组中给定元素的第一个索引,否则返回 -1。
lastIndexOf() 返回数组中给定元素的最后一个索引,否则返回 -1。
撤销() 返回反转的数组。
forEach() 为每个数组元素执行一次提供的函数。
地图() 使用对调用数组中每个元素调用提供的函数的结果来创建一个新数组。
筛选() 创建一个新数组,其中包含通过所提供函数实现的测试的所有元素。
减少() 对累加器和数组中的每个元素应用一个函数,以将其减少为单个值。

为了更好地理解高阶函数及其具体方法map()您可以查看这篇文章filter()reduce()

在开始实施这些方法之前,我们将快速了解一下它们如何prototype工作this

什么是原型?

在 JavaScript 中,每个函数和对象默认都有一个名为“prototype”的属性。原型是 JavaScript 对象相互继承方法和属性的机制。当我们想要为一个对象添加新属性,并在所有实例之间共享时,原型非常有用。

function User () {
    this.name = 'George',
    this.age = 23
}

User.prototype.email = 'george@email.com';
User.prototype.userInfo = function () {
    console.log('[User name]: ', this.name, ' [User age]: ', this.age);
}

const user = new User();

console.log(user.email); // george@email.com

user.userInfo(); // [User name]:  George  [User age]:  23

Enter fullscreen mode Exit fullscreen mode

在上面的例子中,我们创建了User具有属性name和 的函数对象age。然后,我们访问User具有 属性 的函数对象prototype,并将 属性email和 函数添加userInfo()到其中。

这是什么?

的值由当前拥有该关键字所在this空间的对象决定(运行时绑定)。this

function User () {
    this.name = 'George',
    this.age = 23,
    this.printInfo = function() {
        console.log(this);
    }
    this.orders = {
        orderId: '12345',
        printOrderId: function() {
            console.log(this);
        }
    }
}

const user = new User();

user.printInfo(); // User { name: 'George', age: 23, printInfo: [Function], orders: { orderId: '12345', printOrderId: [Function: printOrderId] } }

user.orders.printOrderId(); // { orderId: '12345', printOrderId: [Function: printOrderId] }
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,我们再次使用函数对象User并将该对象添加orders到其中。user.printInfo()打印出该this值,在本例中,它包含函数对象的所有属性Useruser.orders.printOrderId()只打印对象的属性orders,这是因为方法printOrderId()是通过orders对象调用的。

让我们实现数组方法

为了实现这些方法,我们将Array通过prototype属性访问对象,然后添加新的方法。this方法中的关键字 具有调用相应数组方法的数组的值。

自定义 indexOf

Array.prototype.customIndexOf = function (value) {
    for (let i = 0; i < this.length; i++) {
        if (this[i] == value)
            return i;        
    }
    return -1;
}

const output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

console.log(output.customIndexOf(2)); // 1
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,该customIndexOf方法将一个值作为参数,然后我们迭代数组直到找到相应的值并返回其索引。

自定义 lastIndexOf

Array.prototype.customLastIndexOf = function (value) {
    for (let i = this.length - 1; i >= 0; i--) {
        if (this[i] == value)
            return i;        
    }
    return -1;
}

const output = [1, 2, 3, 4, 5, 9, 7, 9, 9, 10];

console.log(output.customLastIndexOf(9)); // 8
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,该customLastIndexOf方法将一个值作为参数,然后我们迭代数组,直到找到最后一个对应的值并返回其索引。

自定义反向

Array.prototype.customReverse = function () {
    let left = 0;
    let right = this.length - 1;

    while(left < right) {
        let temp = this[left];
        this[left] = this[right];
        this[right] = temp;
        left++;
        right--;
    }
    return this;
}

const output = [1, 'b', 'abc', { name: 'Jonh' }, 10];

console.log(output.customReverse()); // [10, { name: 'Jonh' }, 'abc', 'b', 1]
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,该customReverse方法将数组反转并返回它。

自定义 forEach

Array.prototype.customForEach = function (callback) {
    for (let i = 0; i < this.length; i++) {
        callback(this[i], i, this);
    }
}

const output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

output.customForEach(elem => {
    console.log(elem);
}); // 1 2 3 4 5 6 7 8 9 10
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,该customForEach方法将一个回调函数作为参数,并将其应用于数组中的每个元素。此外,回调函数还会接收索引和数组本身,以便在需要时使用。

自定义地图

Array.prototype.customMap = function map(callback) {
    const results = [];
    for (let i = 0; i < this.length; i++) {
        results.push(callback(this[i], i, this));
    }
    return results;
}

let output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

output = output.customMap(elem => {
    return 3*elem;
});

console.log(output); // [ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,该customMap方法将一个回调函数作为参数,对于数组中的每个元素,我们都调用该回调函数,并将结果返回到一个新的数组中。同样,回调函数还会接收索引和数组本身,以便在需要时使用。

自定义过滤器

Array.prototype.customFilter = function (callback) {
    const results = [];
    for (let i = 0; i < this.length; i++) {
        if(callback(this[i], i, this))
            results.push(this[i]);
    }
    return results;
}

let output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

output = output.customFilter((elem) => {
    return elem % 2 === 0;
});

console.log(output); // [ 2, 4, 6, 8, 10 ]
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,该customFilter方法将回调函数作为参数,对于数组中的每个元素,我们都应用回调函数,对于传递回调函数的值,我们将结果返回到新数组中。

自定义减少

Array.prototype.customReduce = function (callback, initialValue) {
    let value = initialValue;

    for (let i = 0; i < this.length; i++) {
        value = callback(value, this[i]);
    }

    return value;
}

const output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const sum = output.customReduce((acc = 0, elem) => {
    return acc + elem;
});

console.log(sum); // 55
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,该customReduce方法将回调函数和累加器变量作为参数,我们将回调函数应用于数组中每个元素的累加器,直到将其减少为单个值。

您可以在此处查看我的 github 存储库

资源

文章来源:https://dev.to/zagaris/implement-javascript-array-methods-from-scratch-2pl1
PREV
理解 JavaScript 中的 map()、filter() 和 reduce()
NEXT
记住 { mutableStateOf() } – 备忘单