写作不易,Star是最大鼓励,感觉写的不错的可以给个Star⭐,请多多指教。Github地址

[TOC]

forEach

作用:遍历数组的每个元素,对其执行一次回调函数,而且不会改变原数组(原数组是对象数组的情况会修改)。基本用法:

// 参数分别为当前数组元素、索引和数组本身
[].forEach((item, index, array) => {});
1
2
const arr1 = [2, 5, 9, 2];
const arr2 = [
    {name: 'lisi', age: 12},
    {name: 'wangwu', age: 13}
];

arr1.forEach(item => item *= 2); // 不会修改原数组
arr2.forEach(item => item.age += 1); // 特殊场景:原数组是对象数组

console.log(arr1); // [ 2, 5, 9, 2 ]
console.log(arr2); // [ { name: 'lisi', age: 13 }, { name: 'wangwu', age: 14 } ]
1
2
3
4
5
6
7
8
9
10
11

实现

Array.prototype.myForEach = function(cb) {
    if(!Array.isArray(this)) {
        return 'TypeError';
    }
    const {length} = this;
    for (let i = 0; i < length; i++) {
        cb(this[i], i, this);
    }
}
// 应用
const arr = [1, 2];
arr.myForEach((item, i, arr) => {
    console.log(item, i, arr);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14

forEach不支持链式操作(没有返回值)

[2, 5, 9, 2].forEach(item => {
    item *= 2;
}).filter(item => {
    return item > 2;
});
// TypeError: Cannot read property 'filter' of undefined
1
2
3
4
5
6

因为forEach并没有返回原数组。

应用

实现一个简单的函数,它能更好地解释每个方法的功能:接受什么作为输入,返回什么,以及它是否对数组进行了修改。

function logOperation(operationName, array, callback) {
    const input = [...array];
    const result = callback(array);

    console.log({
    operation: operationName,
    arrayBefore: input,
    arrayAfter: array,
    mutates: mutatesArray(input, array), // shallow check(是检查了一层,没有考虑数组元素是对象的情况)
    result,
    });
}
1
2
3
4
5
6
7
8
9
10
11
12

mutatesArray方法用来判断是否更改了原数组,如果有修改刚返回true,否则返回 false。

function mutatesArray(firstArray, secondArray) {
  if (firstArray.length !== secondArray.length) {
    return true;
  }

  for (let index = 0; index < firstArray.length; index += 1) {
    if (firstArray[index] !== secondArray[index]) {
      return true;
    }
  }

  return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

使用logOperation来测试前面实现的forEach方法。

logOperation('forEach', [1, 2, 3, 4, 5], array => forEach(array, value => console.log(value)));
1

结果如下:

{
  operation: 'forEach',
  arrayBefore: [ 1, 2, 3, 4, 5 ],
  arrayAfter: [ 1, 2, 3, 4, 5 ],
  mutates: false,
  result: undefined
}
1
2
3
4
5
6
7

map(映射)

map方法会将原数组映射成对应的新数组,对原数组中的每个元素都按顺序调用一次callback回调函数,每次执行后的返回值(包括undefined)组合起来形成一个新数组。 用法如下:

[].map((item, index, array) => {});
1

举个🌰:

const arr = [1, 2, 3, 4];
const arrOfSquares = arr.map(function (item) {
    return item * item;
});
console.log(arrOfSquares); // 1, 4, 9, 16
1
2
3
4
5

需要注意:map方法返回的是一个新的数据,并不会修改原来的数组。

实现

Array.prototype.myMap = function(cb) {
    if(!Array.isArray(this)) {
        return 'Type Error';
    }
    const newArr = [];
    for (let i = 0; i < this.length; i++) {
        // 原生map的回调支持3个参数,数据项,数据项索引,当前数组
        newArr.push(cb(this[i], i, this));
    }
    return newArr;
};

const arr = [{
    name: 'lisi',
    age: 20
}, {
    name: 'wangwu',
    age: 22
}];

const resName = arr.myMap(item => {
    return item.name;
});

const resAge = arr.myMap(item => {
    return item.age;
});
console.log(resName); // [ 'lisi', 'wangwu' ]
console.log(resAge); // [ 20, 22 ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

map方法的回调函数接受旧值作为参数,并返回一个新值,并将其保存在新数组中的相同索引下。

filter

filter方法将回调函数返回为false(返回值只要弱等于(==)Boolean即可)的值过滤调,剩下的值都保存在一个新的数组中,然后返回。 用法:

[].filter(function(item, index, array) {});
1

举个🌰:

const arr = [1, 2, 3, 4];
const arrFilter = arr.filter(item => {
    return item > 2;
});
console.log(arrFilter); // [3, 4]
1
2
3
4
5

链式调用

使用数组方法的好处是:可以将操作链接在一起。举个🌰:

function getTodosWithCategory(todos, category) {
 return todos
   .filter(todo => todo.category === category)
   .map(todo => normalizeTodo(todo));
}
1
2
3
4
5

通过上述的方式,就不必将map的执行结果保存到变量中,代码会更简洁。

实现

Array.prototype.myFilter = function (cb) {
    if(!Array.isArray(this)) {
        return 'Type Error';
    }
    const newArr = [];
    for (let i = 0; i < this.length; i++) {
        // 与map方法实现类似,多加了一个判断条件
        if (cb(this[i], i, this)) {
            newArr.push(this[i]);
        }
    }
    return newArr;
}

const arr = [{
    name: 'lisi',
    age: 20
}, {
    name: 'wangwu',
    age: 22
}];

const resAge = arr.myFilter(item => {
    return item.age > 20;
});

console.log(resAge); // [ { name: 'wangwu', age: 22 } ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

reduce和reduceRight

reduce 是JavaScript 1.8 中才引入的,中文意思为“归约”。基本用法:reduce(callback[, initialValue])

callback 函数接受4个参数:之前值(previousValue)、当前值(currentValue)、索引值(currentIndex)以及数组本身(array)。

可选的初始值(initialValue),作为第一次调用回调函数时传给previousValue的值。也就是,为累加等操作传入起始值(额外的加值)。

var sum = [1, 2, 3, 4].reduce(function (previous, current, index, array) {
    return previous + current;
});
console.log(sum); // 10
1
2
3
4

解析:

因为initialValue不存在,因此一开始的previous值等于数组的第一个元素 从而 current 值在第一次调用的时候就是2 最后两个参数为索引值 index 以及数组本身 array 以下为循环执行过程:

// 初始设置
previous = initialValue = 1, current = 2

// 第一次迭代
previous = (1 + 2) =  3, current = 3

// 第二次迭代
previous = (3 + 3) =  6, current = 4

// 第三次迭代
previous = (6 + 4) =  10, current = undefined (退出)
1
2
3
4
5
6
7
8
9
10
11

reduce实现

Array.prototype.myReduce = function(cb, initialValue) {
    const {length} = this;
    let result = initialValue;
    let startIndex = 0;
    // 检查initialValue是否为undefined。如果是,则将数组的第一项设置为初始值,并将startIndex设置为1。
    if (initialValue === undefined) {
        result = this[0];
        startIndex = 1;
    }
    for (let i = startIndex; i < length; i++) {
        // 每次迭代,reduce方法都将回调函数的结果保存在累加器中,然后在下一次迭代中使用
        result = cb(result, this[i], i, this);
    }
    return result;
}

const arr = [1, 2, 3, 4, 5];
// accumulator为初始值,没有初始值的话为数组的第一个元素
const res = arr.myReduce((accumulator, currentValue, currentIndex, sourceArray) => {
    return accumulator + currentValue;
});
const res2 = arr.myReduce((accumulator, currentValue, currentIndex, sourceArray) => {
    return accumulator + currentValue;
}, 10);

console.log(res); // 15
console.log(res2); // 25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

reduce实现二维数组的扁平化

var matrix = [
  [1, 2],
  [3, 4],
  [5, 6]
];

// 二维数组扁平化
var flatten = matrix.reduce(function (previous, current) {
    return previous.concat(current);
});
console.log(flatten); // [1, 2, 3, 4, 5, 6]
1
2
3
4
5
6
7
8
9
10
11

reduceRight 跟 reduce 相比,用法类似。实现上差异在于reduceRight是从数组的末尾开始实现。

const data = [1, 2, 3, 4];
const specialDiff = data.reduceRight(function (previous, current, index) {
    if (index == 0) {
        return previous + current;
    }
    return previous - current;
});
console.log(specialDiff);  // 0
1
2
3
4
5
6
7
8

every

every方法测试一个数组内的所有元素是否都能通过某个指定函数的测试,它返回一个布尔值。 举个🌰:

[1, 2, 3].every(value => Number.isInteger(value)); // true
1

实现

Array.prototype.myEvery = function(cb) {
    if(!Array.isArray(this)) {
        return 'Type Error';
    }
    let index = 0;
    while (index < this.length) {
        if (!cb(this[index], index, this)) {
            return false;
        }
        index++;
    }
    return true;
}

const arr = [3, 6, 9];

const res = arr.myEvery(item => {
    return item > 4;
});

console.log(res);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

some

some方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。具体参考:MDN

[1, 2, 3].some(value => value > 2); // true
1

实现

Array.prototype.mySome = function(cb) {
    if(!Array.isArray(this)) {
        return 'Type Error';
    }
    for (let i = 0; i < this.length; i++) {
        if (cb(this[i], i, this)) {
            return true;
        }
    }
    return false;
}

const arr = [3, 6, 9];
const res = arr.mySome(item => item > 7);
console.log(res);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

findIndex和find

findIndex用法,来看个🌰:

[1, 2, 3].findIndex(value => value === 2); // 1
1

findIndex方法用于找到数组中给定元素的索引值。该方法对数组中的每一项的索引(0到length-1)执行一次callbac回调k函数,直到找到一个callback函数返回为true的值。如果找到,findIndex返回该项的索引。如果没有找到或者数组的长度为0,则返回-1。

findIndex实现

Array.prototype.myFindIndex = function(cb) {
    if(!Array.isArray(this)) {
        return 'TypeError';
    }
    const {length} = this;
    for (let i = 0; i < length; i++) {
        if (cb(this[i])) {
            return i;
        }
    }
    return -1;
}

const arr = [1, 2, 3, 4];
const index = arr.myFindIndex(item => item === 3);
console.log(index); // 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

find实现

find与findIndex的唯一区别在于:find返回的是找到的数组元素,而不是数组元素对应的索引。

Array.prototype.myFind = function(cb) {
    if(!Array.isArray(this)) {
        return 'TypeError';
    }
    const {length} = this;
    for (let i = 0; i < length; i++) {
        if (cb(this[i])) {
            return this[i];
        }
    }
    // 找不到则返回undefined
    return;
}

const arr = [1, 2, 3, 4];
const target = arr.myFind(item => {
    return item === 3;
});
console.log(target); // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

基于findIndex实现find:

Array.prototype.myFind = function(cb) {
    if(!Array.isArray(this)) {
        return 'TypeError';
    }
    const index = this.findIndex(cb);
    if (index === -1) {
        return undefined;
    }
    return this[index];
}

const arr = [1, 2, 3, 4];
const target = arr.myFind(item => {
    return item === 3;
});
console.log(target); // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

indexOf和lastIndexOf

indexOf

indexOf方法在字符串方法中就有,string.indexOf(searchString, position)。数组里的indexOf方法与之类似,在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。

arr.indexOf(searchElement, fromIndex)
1

fromIndex可选,表示从这个位置开始查找。具体参考MDN

const arr = [1, 2, 5, 4, 5];
console.log(arr.indexOf(2)); // 1
console.log(arr.indexOf(5, 3)); // 4
console.log(arr.indexOf(6)); // -1
1
2
3
4

实现

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

const arr = [1, 2, 3];
console.log(arr.myIndexOf(2)); // 1
console.log(arr.myIndexOf(6)); // -1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

lastIndexOf

lastIndexOf方法返回指定元素在数组中的最后一个的索引,如果不存在则返回-1。从数组的后面向前查找,从fromIndex处开始。

arr.lastIndexOf(searchElement, fromIndex)
1
const arr = [2, 3, 6, 2];
arr.lastIndexOf(2); // 3
arr.lastIndexOf(5); // -1
arr.lastIndexOf(2, 3); // 3
arr.lastIndexOf(2, 2); // 0
arr.lastIndexOf(2, -2); // 0 如果为负值,将其视为从数组末尾向前的偏移
arr.lastIndexOf(2, -1); // 3
1
2
3
4
5
6
7

lastIndexOf实现

Array.prototype.myLastIndexOf = function(searchedValue) {
    if(!Array.isArray(this)) {
        return 'TypeError';
    }
    for (let i = this.length - 1; i >= 0; i--) {
        if (this[i] === searchedValue) {
            return i;
        }
    }
    return -1;
}

const arr = [2, 3, 6, 2];
console.log(arr.lastIndexOf(2)); // 3
console.log(arr.lastIndexOf(5)); // -1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

includes

includes方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。具体参考:MDN

const arr = [1, 2, 3];
arr.includes(2); // true
arr.includes(4); // false
1
2
3

另外,它还可以用于优化||的判断写法。

if (method === 'post' || method === 'put' || method === 'delete') {
}
// 用includes 优化 `||` 的写法
if (['post', 'put', 'delete'].includes(method)) {
}
1
2
3
4
5

includes实现

Array.prototype.myIncludes = function(searchValue) {
    if(!Array.isArray(this)) {
        return 'TypeError';
    }
    for (let i = 0; i < this.length; i++) {
        if (this[i] === searchValue) {
            return true;
        }
    }
    return false;
}

const arr = [1, 2, 3];
console.log(arr.myIncludes(2)); // true
console.log(arr.myIncludes(4)); // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

concat

concat方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

[1, 2, 3].concat([4, 5], 6, [7, 8]) // -> [1, 2, 3, 4, 5, 6, 7, 8]
1

concat实现

Array.prototype.myConcat = function(...values) {
    const result = [...this];
    for (let i = 0; i < values.length; i++) {
        // 如果是数组,再次遍历依次push到结果数组中
        if (Array.isArray(values[i])) {
            for (let j = 0; j < values[i].length; j++) {
                result.push(values[i][j]);
            }
        }
        else {
            result.push(values[i]);
        }
    }
    return result;
}
const arr = [1, 2];
console.log(arr.concat([3, 4, 5], 6, [7, 8]));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

join

join方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。 默认使用','号分割,不改变原数组。

const arr = [1, 2, 3];
console.log(arr.join()); // 2,3,4
console.log(arr);  // [2, 3, 4]
1
2
3

join实现

Array.prototype.myJoin = function(joinWith) {
    // 利用reduce遍历所提供的数组并将结果字符串拼接在一起,在数组的值之间放置所需的分隔符(作为joinWith传递)。
    return this.reduce((result, current, index) => {
        if (index === 0) {
            return current;
        }
        return `${result}${joinWith}${current}`;
    }, '');
}

const arr = [1, 2, 4];
console.log(arr.myJoin('!')); // 1!2!4
1
2
3
4
5
6
7
8
9
10
11
12

array[0]值需要一些特殊的处理,因为此时result是一个空字符串,不希望分隔符(joinWith)位于第一个元素前面。

reverse

reverse方法将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组。

const arr = [1, 2, 3];
console.log(arr.reverse()); // [3, 2, 1]
console.log(arr); // [3, 2, 1]
1
2
3

reverse实现

Array.prototype.myReverse = function() {
    const result = [];
    for (let i = this.length - 1; i >= 0; i--) {
        result.push(this[i]);
    }
    return result;
}

let arr = [1, 2, 3];
console.log(arr.myReverse()); // [3, 2, 1]
1
2
3
4
5
6
7
8
9
10

sort

sort方法按照Unicode code位置排序,默认排序顺序是在将元素转换为字符串后按升序排列。

const fruit = ['cherries', 'apples', 'bananas'];
fruit.sort(); // ['apples', 'bananas', 'cherries']

const scores = [1, 10, 21, 2];
scores.sort();
console.log(scores); // [1, 10, 2, 21]
1
2
3
4
5
6

sort函数传参

const scores = [1, 10, 21, 2];
// 升序
scores.sort((a, b) => a - b);
console.log(scores); // [1, 2, 10, 21]
1
2
3
4
const scores = [1, 10, 21, 2];
// 降序
scores.sort((a, b) => b - a);
console.log(scores); // [21, 10, 2, 1]
1
2
3
4

push和pop

push方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度,会改变原数组。

const arr = [2, 3, 4];
const res = arr.push(5);
console.log(arr); // [2, 3, 4, 5]
console.log(res); // 4
1
2
3
4

push实现

Array.prototype.myPush = function(...values) {
    const {length} = this;
    const {length: valuesLength} = values;
    // 遍历values并将其中的元素添加到原数组中
    for (let i = 0; i < valuesLength; i++) {
        this[length + i] = values[i];
    }
    return this.length; // 返回当前数组长度
}
const arr = [2, 3, 4];
const res = arr.myPush(5);
console.log(arr); // [2, 3, 4, 5]
console.log(res); // 4
1
2
3
4
5
6
7
8
9
10
11
12
13

pop方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。

const arr = [1, 2, 3, 4];
console.log(arr.pop()); // 4
console.log(arr);  // [1, 2, 3]
1
2
3

pop实现

Array.prototype.myPop = function() {
    // 获取数组的最后一个元素
    const value = this[this.length - 1];
    // 将数组的长度减少1,从而删除最后一个元素
    this.length -= 1;
    return value;
}
const arr = [1, 2, 3, 4];
console.log(arr.myPop()); // 4
console.log(arr);  // [1, 2, 3]
1
2
3
4
5
6
7
8
9
10

shift和unshift

shift方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。

const arr = [2, 3, 4];
console.log(arr.shift()); // 2
console.log(arr); // [3,4]
1
2
3

shift实现

Array.prototype.myShift = function(value) {
    const firstValue = this[0];
    for (let i = 1; i < this.length; i++) {
        // 遍历数组并将每个值向前移动一个索引
        this[i - 1] = this[i];
    }
    // 更新数组的长度并返回初始值
    this.length -= 1;
    return firstValue;
}

const arr = [1, 2, 3];
console.log(arr.myShift()); // 1
console.log(arr); // [ 2, 3 ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14

unshift方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。

const arr = [1, 2, 3, 4, 5];
console.log(arr.unshift(6, 7)); // 7
console.log(arr); // 6, 7, 1, 2, 3, 4, 5]
1
2
3

unshift实现

Array.prototype.myUnshift = function(...values) { // values可能是多个值,因此这里用...接收
    // values需要放到新数组的前面
    const mergedArr = values.concat(this);
    const {length} = mergedArr;
    // 获取新数组的长度并遍历新数组,将它的值保存在原始数组中,并覆盖开始时的值。
    for (let i = 0; i < length; i++) {
        this[i] = mergedArr[i];
    }
    return this.length;
}

const arr = [1, 2, 3];
console.log(arr.myUnshift(4, 5, 6)); // 6
console.log(arr); // [ 4, 5, 6, 1, 2, 3 ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14

slice和splice

slice方法返回一个新的数组对象,这一对象是一个由begin和end决定的原数组的浅拷贝(包括begin,不包括end)。原始数组不会被改变。

const arr = [1, 2, 3, 4, 5];
console.log(arr.slice(1, 3));  // [2, 3]
console.log(arr); // [1, 2, 3, 4, 5]
1
2
3

slice实现

// 这里使用了默认参数,这样当没有传递参数时,slice方法将返回原数组的副本
Array.prototype.mySlice = function(startIndex = 0, endIndex = this.length) {
    const result = [];
    for (let i = startIndex; i < endIndex; i++) {
        result.push(this[i]);
    }
    return result;
}

const arr = [1, 2, 3, 4, 5];
console.log(arr.mySlice(1, 3));  // [2, 3]
console.log(arr); // [1, 2, 3, 4, 5]
1
2
3
4
5
6
7
8
9
10
11
12

splice方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

const arr = [5, 6, 7, 8];
console.log(arr.splice(1, 2, 3));  // [6, 7]
console.log(arr); // [5, 3, 8]
1
2
3

splice实现

/**
 * insertIndex:插入元素的位置
 * removeNumbers:从插入位置开始删除指定数量的元素
 * values:要插入的元素
*/
Array.prototype.mySplice = function(insertIndex, removeNumbers, ...values) {
    const firstPart = this.slice(0, insertIndex);
    const secondPart = this.slice(insertIndex + removeNumbers);
    const removeElements = this.slice(insertIndex, insertIndex + removeNumbers);

    const joinedArr = firstPart.concat(values, secondPart);
    const {length} = joinedArr;
    for (let i = 0; i < length; i++) {
        this[i] = joinedArr[i];
    }
    this.length = length;
    return removeElements; // 返回被删除的元素
}

const arr = [5, 6, 7, 8];
console.log(arr.mySplice(1, 2, 3));  // [6, 7]
console.log(arr); // [5, 3, 8]
::: tip
实现思路:在insertAtIndex和insertAtIndex + removeNumbers上进行两次切割。将原数组切成三段。第一部分(firstPart)和第三部分(secondPart)加上插入的元素组成为最后数组的内容。
:::
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

fill

fill方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。 基本用法:

[].fill(value, start, end)
1

该函数有三个参数:填充值(value),起始索引(start,可以省略,默认值为0),终止索引(end,可以省略,默认值为 this.length)。具体详见:MDN

// 采用一个默认值,填充数组
const arr1 = [1, 2, 3, 4, 5];
arr1.fill(6);
console.log(arr1); // [ 6, 6, 6, 6, 6 ]

// 指定开始和结束位置填充,包含头不包含尾。
const arr2 = [1, 2, 3, 4, 5];
arr2.fill(6, 2, 4);
console.log(arr2); // [1, 2, 6, 6, 5]

// 结束位置省略,从起始位置到最后。
const arr3 = [1, 2, 3, 4, 5];
arr3.fill(6, 2);
console.log(arr3); // [1, 2, 6, 6, 6]
1
2
3
4
5
6
7
8
9
10
11
12
13
14

fill方法作用是:替换指定索引范围内的数组的值。如果没有提供范围,将替换数组的所有元素。

fill实现

Array.prototype.myFill = function(value, startIndex = 0, endIndex = this.length) {
    for (let i = startIndex; i < endIndex; i++) {
        this[i] = value;
    }
    return this;
}

const arr = [1, 2, 3, 4, 5];
const res = arr.myFill(6);
console.log(arr); // [ 6, 6, 6, 6, 6 ]
console.log(res); // [ 6, 6, 6, 6, 6 ]
1
2
3
4
5
6
7
8
9
10
11

flat

flat方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

// 因为展开的深度值是1,所以只有第一级数组是被扁平,其余的保持不变。
[1, 2, 3, [4, 5, [6, 7, [8]]]].flat(1); // -> [1, 2, 3, 4, 5, [6, 7, [8]]]
1
2

flat实现

function flat(array, depth = 0) {
    // 检查depth参数是否小于1。小于1无需扁平化,直接返回原数组
    // 且如果array不是数组,则直接返回
    if (depth < 1 || !Array.isArray(array)) {
        return array;
    }
    // 使用reduce方法。从一个空数组开始,然后取数组的每个值并将其扁平
    return array.reduce(
        (result, current) => {
            // 调用带有(depth - 1)的flat函数。每次调用时,都递减depth参数,以免造成无限循环。扁平化完成后,将返回值来回加到result数组中。
            return result.concat(flat(current, depth - 1));
        }, []);
}

const arr = [1, 2, 3, [4, 5, [6]]];
console.log(flat(arr, 1)); // [ 1, 2, 3, 4, 5, [ 6 ] ]
console.log(arr); // [ 1, 2, 3, [ 4, 5, [ 6 ] ] ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

flatMap

flatMap方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与map连着深度值为1的flat几乎相同,但flatMap通常在合并成一种方法的效率稍微高一些。 在map方法中,对于每个值,只返回一个值。这样,一个包含三个元素的数组在映射之后仍然有三个元素。使用flatMap,在提供的回调函数中,可以返回一个数组,这个数组稍后将被扁平。

[1, 2, 3].flatMap(value => [value, value, value]); // [1, 1, 1, 2, 2, 2, 3, 3, 3]
1

flatMap实现

function flatMap(arr, callback) {
    return arr.map(callback).flat(1);
}

const arr = [1, 2, 3]
const res = flatMap(arr, value => [value, value, value]);
console.log(res); // [1, 1, 1, 2, 2, 2, 3, 3, 3]
console.log(arr); // [1, 2, 3]
1
2
3
4
5
6
7
8

values

values方法返回一个新的Array Iterator 对象(values方法返回一个生成器),该对象包含数组每个索引的值。具体参考:MDN

const array1 = ['a', 'b', 'c'];
const iterator = array1.values();

for (const value of iterator) {
  console.log(value); // expected output: "a" "b" "c"
}
1
2
3
4
5
6

values实现

function values(array) {
 const { length } = array;

 function* createGenerator() {
   for (let index = 0; index < length; index += 1) {
     const value = array[index];
     yield value;
   }
 }

 return createGenerator();
}
1
2
3
4
5
6
7
8
9
10
11
12

keys

keys方法返回一个包含数组中每个索引键的Array Iterator对象。

var array1 = ['a', 'b', 'c'];
var iterator = array1.keys();

for (let key of iterator) {
  console.log(key); // expected output: 0 1 2
}
1
2
3
4
5
6

keys实现

function keys(array) {
 function* createGenerator() {
   const { length } = array;

   for (let index = 0; index < length; index += 1) {
     yield index;
   }
 }

 return createGenerator();
}
1
2
3
4
5
6
7
8
9
10
11

entries

entries方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对。

var array1 = ['a', 'b', 'c'];

var iterator1 = array1.entries();

console.log(iterator1.next().value);
// expected output: Array [0, "a"]

console.log(iterator1.next().value);
// expected output: Array [1, "b"]
1
2
3
4
5
6
7
8
9

entries实现

function entries(array) {
 const { length } = array;

 function* createGenerator() {
   for (let index = 0; index < length; index += 1) {
     const value = array[index];
     yield [index, value];
   }
 }

 return createGenerator();
}
1
2
3
4
5
6
7
8
9
10
11
12

isArray

Array.isArray() 用于确定传递的值是否是一个 Array。一个比较冷门的知识点:其实 Array.prototype 也是一个数组。

Array.isArray([]); // true
Array.isArray(Array.prototype); // true

Array.isArray(null); // false
Array.isArray(undefined); // false
Array.isArray(18); // false
Array.isArray('Array'); // false
Array.isArray(true); // false
Array.isArray({ __proto__: Array.prototype });
1
2
3
4
5
6
7
8
9

在公用库中,一般会这么做 isArray 的判断:

Object.prototype.toString.call(arg) === '[object Array]';
1

from

将类似数组的对象(array-like object)和可遍历(iterable)的对象转为真正的数组。

const set = new Set(1, 2, 3, 3, 4);
Array.from(set)  // [1, 2, 3, 4]

Array.from('foo'); // ["f", "o", "o"]
1
2
3
4

of

Array.of方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。

Array.of和Array构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个长度为7的空数组(注意:这是指一个有7个空位的数组,而不是由7个undefined组成的数组)。

Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]

Array(7); // [ , , , , , , ]
Array(1, 2, 3); // [1, 2, 3]
1
2
3
4
5

copyWithin

选择数组的某个下标,从该位置开始复制数组元素,默认从0开始复制。也可以指定要复制的元素范围。基本用法:[].copyWithin(target, start, end)

const arr = [1, 2, 3, 4, 5];
console.log(arr.copyWithin(3));
 // [1, 2, 3, 1, 2] 从下标为3的元素开始,复制数组,所以4, 5被替换成1, 2

const arr1 = [1, 2, 3, 4, 5];
console.log(arr1.copyWithin(3, 1));
// [1,2,3,2,3] 从下标为3的元素开始,复制数组,指定复制的第一个元素下标为1,所以4, 5被替换成2, 3

const arr2 = [1, 2, 3, 4, 5];
console.log(arr2.copyWithin(3, 1, 2));
// [1,2,3,2,5] 从下标为3的元素开始,复制数组,指定复制的第一个元素下标为1,结束位置为2,所以4被替换成2
1
2
3
4
5
6
7
8
9
10
11

entries、values 和 keys

entries返回迭代器:返回键值对

//数组
const arr = ['a', 'b', 'c'];
for(let v of arr.entries()) {
    console.log(v)
}
// [0, 'a'] [1, 'b'] [2, 'c']

//Set
const arr = new Set(['a', 'b', 'c']);
for(let v of arr.entries()) {
    console.log(v)
}
// ['a', 'a'] ['b', 'b'] ['c', 'c']

//Map
const arr = new Map();
arr.set('a', 'a');
arr.set('b', 'b');
for(let v of arr.entries()) {
    console.log(v)
}
// ['a', 'a'] ['b', 'b']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

values返回迭代器:返回键值对的 value

//数组
const arr = ['a', 'b', 'c'];
for(let v of arr.values()) {
    console.log(v)
}
//'a' 'b' 'c'

//Set
const arr = new Set(['a', 'b', 'c']);
for(let v of arr.values()) {
    console.log(v)
}
// 'a' 'b' 'c'

//Map
const arr = new Map();
arr.set('a', 'a');
arr.set('b', 'b');
for(let v of arr.values()) {
    console.log(v)
}
// 'a' 'b'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

keys返回迭代器:返回键值对的 key

//数组
const arr = ['a', 'b', 'c'];
for(let v of arr.keys()) {
    console.log(v)
}
// 0 1 2

//Set
const arr = new Set(['a', 'b', 'c']);
for(let v of arr.keys()) {
    console.log(v)
}
// 'a' 'b' 'c'

//Map
const arr = new Map();
arr.set('a', 'a');
arr.set('b', 'b');
for(let v of arr.keys()) {
    console.log(v)
}
// 'a' 'b'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

修改原数组方法

  • forEach:当数组元素为引用类型时会修改原数组,数据元素为基本类型时不修改;
  • sort
  • splice
  • push
  • pop
  • shift
  • unshift
  • reverse

不修改原数组方法

  • map
  • filter
  • some
  • every
  • find
  • findIndex
  • concat(返回一个新数组)
  • join
  • flat
  • flatMap

参考文档

  1. Understand array methods by implementing them — all of them
  2. 通过实现25个数组方法来理解及高效使用数组方法
  3. 「干货」细说 Array 的常用操作(ES5 和 ES6)
  4. 关于JS中一些重要的api实现, 巩固你的原生JS功底