# 通过实现25个数组方法来理解及高效使用数组方法(长文,建议收藏)

·  阅读 28562

``````Array.prototype.map = function map() {
// implementation
};

``````function map(array) {
// implementation
}

``````class OwnArray extends Array {
public constructor(...args) {
super(...args);
}

public map() {
// implementation
return this;
}
}

# 集合类

## .forEach

`Array.prototype.forEach` 方法对数组的每个元素执行一次提供的函数，而且不会改变原数组。

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

``````function forEach(array, callback) {
const { length } = array;

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

``````function getTodosWithCategory(todos, category) {
.filter(todo => todo.category === category)
.map(todo => normalizeTodo(todo));
}

``````// 无法工作
function getTodosWithCategory(todos, category) {
.filter(todo => todo.category === category)
.forEach((value) => console.log(value))
.map(todo => normalizeTodo(todo));
}

#### 帮助函数 （打印信息）

``````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,
});
}

``````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;
}

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

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

## .map

`map` 方法会给原数组中的每个元素都按顺序调用一次 `callback` 函数。`callback` 每次执行后的返回值（包括 `undefined`）组合起来形成一个新数组。

#### 实现

``````function map(array, callback) {
const result = [];
const { length } = array;

for (let index = 0; index < length; index +=1) {
const value = array[index];

result[index] = callback(value, index, array);
}

return result;
}

#### 测试

``````logOperation('map', [1, 2, 3, 4, 5], array => map(array, value => value + 5));

``````{
operation: 'map',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: [ 6, 7, 8, 9, 10 ]
}

## .filter

`Array.prototype.filter` 过滤回调返回为`false`的值，每个值都保存在一个新的数组中，然后返回。

``````[1, 2, 3, 4, 5].filter(number => number >= 3);
// -> [3, 4, 5]

#### 实现

``````function push(array, ...values) {
const { length: arrayLength } = array;
const { length: valuesLength } = values;

for (let index = 0; index < valuesLength; index += 1) {
array[arrayLength + index] = values[index];
}

return array.length;
}
--------------------------------------------------
function filter(array, callback) {
const result = [];

const { length } = array;

for (let index = 0; index < length; index += 1) {
const value = array[index];

if (callback(value, index, array)) {
push(result, value);
}
}

return result;
}

#### 测试

``````logOperation('filter', [1, 2, 3, 4, 5], array => filter(array, value => value >= 2));

``````{
operation: 'filter',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: [ 2, 3, 4, 5 ]
}

## .reduce

`reduce()` 方法接收一个函数作为累加器，数组中的每个值（从左到右）开始缩减，最终计算为一个值`reduce()` 方法接受四个参数：初始值（或者上一次回调函数的返回值），当前元素值，当前索引，调用 reduce() 的数组

`````` [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].reduce((sum, number) => {
return sum + number;
}, 0) // -> 55

#### 实现

``````function reduce(array, callback, initValue) {
const { length } = array;

let acc = initValue;
let startAtIndex = 0;

if (initValue === undefined) {
acc = array[0];
startAtIndex = 0;
}

for (let index = startAtIndex; index < length; index += 1) {
const value = array[index];
acc = callback(acc, value, index, array)
}

return acc;
}

#### 测试

``````logOperation('reduce', [1, 2, 3, 4, 5], array => reduce(array, (sum, number) => sum + number, 0));

``````{ operation: 'reduce',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: 15
}

# 检索类

## .findIndex

`findIndex`帮助咱们找到数组中给定值的索引。

``````[1, 2, 3, 4, 5, 6, 7].findIndex(value => value === 5); // 4

`findIndex`方法对数组中的每个数组索引`0..length-1`（包括）执行一次`callback`函数，直到找到一个`callback`函数返回真实值（强制为`true`）的值。如果找到这样的元素，`findIndex`会立即返回该元素的索引。如果回调从不返回真值，或者数组的`length``0`，则`findIndex`返回`-1`

#### 实现

``````function findIndex(array, callback) {
const { length } = array;

for (let index = 0; index < length; index += 1) {
const value = array[index];

if (callback(value, index, array)) {
return index;
}
}

return -1;
}

#### 测试

``````logOperation('findIndex', [1, 2, 3, 4, 5], array => findIndex(array, number => number === 3));

``````{
operation: 'findIndex',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: 2
}

## .find

`find``findIndex`的唯一区别在于，它返回的是实际值，而不是索引。实际工作中，咱们可以重用已经实现的`findIndex`

``````[1, 2, 3, 4, 5, 6, 7].find(value => value === 5); // 5

#### 实现

``````function find(array, callback) {
const index = findIndex(array, callback);

if (index === -1) {
return undefined;
}

return array[index];
}

#### 测试

``````logOperation('find', [1, 2, 3, 4, 5], array => find(array, number => number === 3));

#### 运行

``````{
operation: 'find',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: 3
}

## .indexOf

`indexOf`是获取给定值索引的另一种方法。然而，这一次，咱们将实际值作为参数而不是函数传递。同样，为了简化实现，可以使用前面实现的`findIndex`

``````[3, 2, 3].indexOf(3); // -> 0

#### 实现

``````function indexOf(array, searchedValue) {
return findIndex(array, value => value === searchedValue)
}

#### 测试

``````logOperation('indexOf', [1, 2, 3, 4, 5], array => indexOf(array, 3));

#### 执行结果

``````{
operation: 'indexOf',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: 2
}

## .lastIndexOf

lastIndexOf的工作方式与`indexOf`相同，`lastIndexOf()` 方法返回指定元素在数组中的最后一个的索引，如果不存在则返回 `-1`

``````[3, 2, 3].lastIndexOf(3); // -> 2

#### 实现

``````function lastIndexOf(array, searchedValue) {
for (let index = array.length - 1; index > -1; index -= 1 ){
const value = array[index];

if (value === searchedValue) {
return index;
}
}
return  -1;
}

#### 测试

``````logOperation('lastIndexOf', [1, 2, 3, 4, 5, 3], array => lastIndexOf(array, 3));

#### 执行结果

``````{
operation: 'lastIndexOf',
arrayBefore: [ 1, 2, 3, 4, 5, 3 ],
arrayAfter: [ 1, 2, 3, 4, 5, 3 ],
mutates: false,
result: 5
}

## .every

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

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

#### 实现

``````function every(array, callback){
const { length } = array;

for (let index = 0; index < length; index += 1) {
const value = array[index];

if (!callback(value, index, array)) {
return false;
}
}

return true;
}

#### 测试

``````logOperation('every', [1, 2, 3, 4, 5], array => every(array, number => Number.isInteger(number)));

#### 执行结果

``````{
operation: 'every',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: true
}

## .some

`some` 方法与 `every` 刚好相反，即只要其中一个为`true` 就会返回`true`。与`every` 方法类似，咱们可以将`some` 方法看作一个等价于逻辑或数组。

``````[1, 2, 3, 4, 5].some(number => number === 5); // -> true

#### 实现

``````function some(array, callback) {
const { length } = array;

for (let index = 0; index < length; index += 1) {
const value = array[index];

if (callback(value, index, array)) {
return true;
}
}

return false;
}

#### 测试

``````logOperation('some', [1, 2, 3, 4, 5], array => some(array, number => number === 5));

#### 执行结果

``````{
operation: 'some',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: true
}

## .includes

`includes`方法的工作方式类似于 `some` 方法，但是`includes`不用回调，而是提供一个参数值来比较元素。

``````[1, 2, 3].includes(3); // -> true

#### 实现

``````function includes(array, searchedValue){
return some(array, value => value === searchedValue)
}

#### 测试

``````logOperation('includes', [1, 2, 3, 4, 5], array => includes(array, 5));

#### 执行结果

``````{
operation: 'includes',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: true
}

# 拼接、附加和反转数组

## .concat

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

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

#### 实现

``````function concat(array, ...values) {
const result = [...array];
const { length } = values;

for (let index = 0; index < length; index += 1) {
const value = values[index];

if (Array.isArray(value)) {
push(result, ...value);
} else {
push(result, value);
}
}

return result;
}

`concat`将数组作为第一个参数，并将未指定个数的值作为第二个参数，这些值可以是数组，也可以是其他类型的值。

`push(result, value)` 只会向数组追加为一个元素。相反，通过使用展开操作符`push(result，…value)` 将数组的所有值附加到`result` 数组中。在某种程度上，咱们把数组扁平了一层。

#### 测试

``````logOperation('concat', [1, 2, 3, 4, 5], array => concat(array, 1, 2, [3, 4]));

#### 执行结果

``````{
operation: 'concat',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: [ 1, 2, 3, 4, 5, 1, 2, 3, 4 ]
}

## .join

`join()` 方法用于把数组中的所有元素放入一个字符串，元素是通过指定的分隔符进行分隔的。

``````['Brian', 'Matt', 'Kate'].join(', ') // -> Brian, Matt, Kate

#### 实现

``````function join(array, joinWith) {
return reduce(
array,
(result, current, index) => {
if (index === 0) {
return current;
}

return `\${result}\${joinWith}\${current}`;
},
''
)
}

`reduce`的回调是神奇之处:`reduce`遍历所提供的数组并将结果字符串拼接在一起，在数组的值之间放置所需的分隔符(作为`joinWith`传递)。

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

#### 测试

``````logOperation('join', [1, 2, 3, 4, 5], array => join(array, ', '));

#### 执行结果

``````{
operation: 'join',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: '1, 2, 3, 4, 5'
}

## .reverse

`reverse()` 方法将数组中元素的位置颠倒，并返回该数组，该方法会改变原数组。

#### 实现

``````function reverse(array) {
const result = []
const lastIndex = array.length - 1;

for (let index = lastIndex; index > -1; index -= 1) {
const value = array[index];
result[lastIndex - index ] = value
}
return result;
}

#### 测试

``````logOperation('reverse', [1, 2, 3, 4, 5], array => reverse(array));

#### 执行结果

``````{
operation: 'reverse',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: [ 5, 4, 3, 2, 1 ]
}

# 添加、删除和追加值

## .shift

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

``````[1, 2, 3].shift(); // -> 1

#### 实现

``````function shift(array) {
const { length } = array;
const firstValue = array[0];

for (let index = 1; index > length; index += 1) {
const value = array[index];
array[index - 1] = value;
}

array.length = length - 1;

return firstValue;
}

#### 测试

``````logOperation('shift', [1, 2, 3, 4, 5], array => shift(array));

#### 执行结果

``````{
operation: 'shift',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 2, 3, 4, 5 ],
mutates: true,
result: 1
}

## .unshift

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

``````[2, 3, 4].unshift(1); // -> [1, 2, 3, 4]

#### 实现

``````function unshift(array, ...values) {
const mergedArrays = concat(values, ...array);
const { length: mergedArraysLength } = mergedArrays;

for (let index = 0; index < mergedArraysLength; index += 1) {
const value = mergedArrays[index];
array[index] = value;
}

return array.length;
}

#### 测试

logOperation('unshift', [1, 2, 3, 4, 5], array => unshift(array, 0));

#### 执行结果

``````{
operation: 'unshift',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 0, 1, 2, 3, 4, 5 ],
mutates: true,
result: 6
}

## .slice

``````slice()

`slice` 会提取原数组中索引从 `begin``end` 的所有元素（包含 `begin`，但不包含 `end`）。

``````[1, 2, 3, 4, 5, 6, 7].slice(3, 6); // -> [4, 5, 6]

#### 实现 (简单实现)

``````function slice(array, startIndex = 0, endIndex = array.length) {
const result = [];

for (let index = startIndex; index < endIndex; index += 1) {
const value = array[index];

if (index < array.length) {
push(result, value);
}
}

return result;
}

#### 测试

``````logOperation('slice', [1, 2, 3, 4, 5], array => slice(array, 1, 3));

#### 执行结果

``````{
operation: 'slice',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4, 5 ],
mutates: false,
result: [ 2, 3 ]
}

## .splice

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

``````const arr = [1, 2, 3, 4, 5];
// 从位置0开始，删除2个元素后插入 3, 4, 5
arr.splice(0, 2, 3, 4, 5);

arr // -> [3, 4, 5, 3, 4, 5]

#### 实现

``````function splice( array, insertAtIndex, removeNumberOfElements, ...values) {
const firstPart = slice(array, 0, insertAtIndex);
const secondPart = slice(array, insertAtIndex + removeNumberOfElements);

const removedElements = slice(
array,
insertAtIndex,
insertAtIndex + removeNumberOfElements
);

const joinedParts = firstPart.concat(values, secondPart);
const { length: joinedPartsLength } = joinedParts;

for (let index = 0; index < joinedPartsLength; index += 1) {
array[index] = joinedParts[index];
}

array.length = joinedPartsLength;

return removedElements;
}

#### 测试

``````logOperation('splice', [1, 2, 3, 4, 5], array => splice(array, 1, 3));

#### 执行结果

``````{
operation: 'splice',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 5 ],
mutates: true,
result: [ 2, 3, 4 ]
}

## .pop

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

#### 实现

``````function pop(array) {
const value = array[array.length - 1];

array.length = array.length - 1;

return value;
}

#### 测试

``````logOperation('pop', [1, 2, 3, 4, 5], array => pop(array));

#### 执行结果

``````{
operation: 'pop',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [ 1, 2, 3, 4 ],
mutates: true,
result: 5
}

## .push

`push()` 方法将一个或多个元素添加到数组的末尾，并返回该数组的新长度。

``````[1, 2, 3, 4].push(5); // -> [1, 2, 3, 4, 5]

#### 实现

``````function push(array, ...values) {
const { length: arrayLength } = array;
const { length: valuesLength } = values;

for (let index = 0; index < valuesLength; index += 1) {
array[arrayLength + index] = values[index];
}

return array.length;
}

#### 测试

``````logOperation('push', [1, 2, 3, 4, 5], array => push(array, 6, 7));

#### 执行结果

``````{
operation: 'push',
arrayBefore: [ 1, 2, 3, 4, 5 ],
arrayAfter: [
1, 2, 3, 4,5, 6, 7
],
mutates: true,
result: 7
}

## .fill

``````[...Array(5)].fill(null) // -> [null, null, null, null, null]

#### 实现

``````function fill(array, value, startIndex = 0, endIndex = array.length) {
for (let index = startIndex; index < endIndex; index += 1) {
array[index] = value;
}

return array;
}

`fill`方法真正做的是替换指定索引范围内的数组的值。如果没有提供范围，该方法将替换所有数组的值。

#### 测试

``````logOperation("fill", [...new Array(5)], array => fill(array, 0));

#### 执行结果

``````{
operation: 'fill',
arrayBefore: [ undefined, undefined, undefined, undefined, undefined ],
arrayAfter: [ 0, 0, 0, 0, 0 ],
mutates: true,
result: [ 0, 0, 0, 0, 0 ]
}

# 扁平类

## .flat

`flat`方法通过可指定深度值来减少嵌套的深度。

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

``````[1, 2, 3, [4, 5]].flat(1) // -> [1, 2, 3, 4, 5]

#### 实现

``````function flat(array, depth = 0) {
if (depth < 1 || !Array.isArray(array)) {
return array;
}

return reduce(
array,
(result, current) => {
return concat(result, flat(current, depth - 1));
},
[],
);
}

#### 测试

``````logOperation('flat', [1, 2, 3, [4, 5, [6]]], array => flat(array, 2));

#### 执行结果

``````{
operation: 'flat',
arrayBefore: [ 1, 2, 3, [ 4, 5, [Array] ] ],
arrayAfter: [ 1, 2, 3, [ 4, 5, [Array] ] ],
mutates: false,
result: [ 1, 2, 3, 4, 5, 6 ]
}

## .flatMap

`flatMap()` 方法首先使用映射函数映射每个元素，然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同，但 flatMap 通常在合并成一种方法的效率稍微高一些。

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

#### 实现

``````function flatMap(array, callback) {
return flat(map(array, callback), 1);
}

#### 测试

``````logOperation('flatMap', [1, 2, 3], array => flatMap(array, number => [number, number]));

#### 执行结果

``````{
operation: 'flatMap',
arrayBefore: [ 1, 2, 3 ],
arrayAfter: [ 1, 2, 3 ],
mutates: false,
result: [ 1, 1, 2, 2, 3, 3 ]
}

# generator 类

## .values

`values`方法返回一个生成器，该生成器生成数组的值。

``````const valuesGenerator = values([1, 2, 3, 4, 5]);

valuesGenerator.next(); // { value: 1, done: false }

#### 实现

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

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

return createGenerator();
}

## .keys

`keys`方法返回一个生成器，该生成器生成数组的索引。

``````const keysGenerator = keys([1, 2, 3, 4, 5]);

keysGenerator.next(); // { value: 0, done: false }

#### 实现

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

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

return createGenerator();
}

## .entries

`entry`方法返回生成键值对的生成器。

``````const entriesGenerator = entries([1, 2, 3, 4, 5]);

entriesGenerator.next(); // { value: [0, 1], done: false }

#### 实现

``````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();
}