JavaScript中部分数组API的使用

186 阅读6分钟

一、引言

  由于对JavaScript中一些数组API的使用及原理不够熟悉,在此进行一些数组Api的练习,首先根据数组Api是否会改变原数组进行分类,然后在分类中进行API的实践,最后尝试根据自己的理解重写这些API。

二、会改变原数组的API

push

意义: 将指定的元素添加到数组的末尾,并返回新数组的长度。

实际应用:

// 例子:向原数组arr,添加两个新元素
const arr = [1,2,3,4,5,6]; 
const pushResult = arr.push(7,8); // push的参数可以为多个,用逗号分隔开
console.log(pushResult); // push的返回值是新数组的长度,此处是8

API重写:

Array.prototype.myPush = function () {
  // arguments是一个对应于传递给函数的参数的类数组对象
  for (let i = 0; i < arguments.length; i++) {
    this[this.length] = arguments[i];
  }
  return this.length; // 返回新数组的长度
};

pop

意义: 从数组中删除最后一个元素,并返回该元素的值。 需注意的是: 如果对一个空数组调用pop(),它会返回undefined。

实际应用:

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

API重写:

Array.prototype.myPop = function () {
  if (this.length) {
    const popElement = this[this.length - 1];
    delete this[this.length - 1];
    this.length--;
    return popElement;
  }
};

shift

意义: 从数组中删除第一个元素,并返回该元素的值。 需注意的是: shift( )将第一个元素删除后,会后续元素的下标依次向前移动。如果对一个空数组调用shift(),它会返回undefined。

实际应用:

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

API重写:

Array.prototype.myShift = function() {
  if (this.length) {
    const shiftElement = this[0];
    for (let index = 0; index < this.length - 1; index++) {
      this[index] = this[index + 1];
    }
    this.length--;
    return shiftElement;
  }
};

unshift

意义: 将指定元素添加到数组的开头,并返回新数组的长度。

实际应用:

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

重写API:

Array.prototype.myUnshift = function () {
  const argumentsLen = arguments.length;
  this.length += argumentsLen;
  for (let index = this.length - 1; index >= 0; index--) {
    if (index > argumentsLen - 1) {
      this[index] = this[index - argumentsLen];
    } else {
      this[index] = arguments[index];
    }
  }
  return this.length;
};

splice

意义: 移除或替换数组中已存在的元素,或是给数组添加新的元素。 参数分析:

start:从零开始计算的索引,表示要开始改变数组的位置。  
1.如果-array.length < start < 0,那么开始索引为start + array.length。   
2.如果start < -array.length,那么开始索引为0.   
3.如果start >= array.length,则不会删除任何元素,此次调用会被理解为向数组添加新的元素。   
4.如果start被省略,则不会删除任何元素。

deleteCount:表示数组中要从start开始删除的元素数量。(可选参数)  
 1.如果省略deleteCount或者deleteCount>array.length-start,那么从start到数组的最后一个元素将被删除。  
 2.如果deleteCount的值是0或是负数,则不会删除任何元素。在这种情况下,你应该至少指定一个新元素。

item1、...、itemN:从start开始要加入到数组的中的元素。(可选参数)

返回值:调用splice方法将会返回一个由被删除元素组成的数组,如果没有删除任何元素,则返回一个空数组。

实际应用:

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

重写API:

Array.prototype.mySplice = function (start, deleteCount, ...items) {
  let deleteItems = [],
    startIndex = 0,
    actualDeleteCount;
​
  if (Object.is(arguments.length, 0)) return deleteItems;
​
  const length = this.length;
​
  startIndex =
    start >= 0 ? Math.min(start, length) : Math.max(length + start, 0);
​
  actualDeleteCount = Object.is(arguments.length, 1)
    ? length - startIndex
    : Math.min(Math.max(+deleteCount, 0), length - startIndex);
​
  deleteItems = this.slice(startIndex, startIndex + actualDeleteCount);
  const otherItems = this.slice(startIndex + actualDeleteCount);
  [...items, ...otherItems].forEach((item) => (this[startIndex++] = item));
  this.length = startIndex;
  return deleteItems;
};

reverse

意义: 反转数组中的元素,并返回该数组。

实际应用:

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

重写API:

Array.prototype.myReverse = function () {
  let left = 0,
    right = this.length - 1;
  while (left < right) {
    const temp = this[left];
    this[left] = this[right];
    this[right] = temp;
    left++;
    right--;
  }
  return this;
};

三、不会改变原数组的API

includes

意义: 用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。 参数分析:

includes(searchElement,fromIndex)意义
searchElement需要查找的值
fromIndex (可选)开始搜索的索引(从0开始)

实际应用:

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

重写API:

Array.prototype.myIncludes = function (searchElement, fromIndex = 0) {
  const length = this.length;
  const startIndex = Math.max(
    fromIndex >= 0 ? fromIndex : length + fromIndex,
    0
  );
  for (let index = startIndex; index < length; index++) {
    if (this[index] === searchElement) {
      return true;
    }
  }
  return false;
};

indexOf

意义: 返回数组中第一次出现给定元素的下标,如果不存在则返回-1. 参数分析:

indexOf(searchElement,fromIndex)意义
searchElement需要查找的值
fromIndex (可选)开始搜索的索引(从0开始)

实际应用:

const arr = [1,2,3,4,5];
const findItemIndex = arr.indexOf(2); // 1

重写API:

Array.prototype.myIndexOf = function (searchElement, fromIndex = 0) {
  let findIndex = -1,
    actualFromIndex;
  if (Object.is(arguments.length, 0)) return findIndex;
​
  if (fromIndex >= this.length) return findIndex;
​
  actualFromIndex = Object.is(arguments.length, 1)
    ? 0
    : Math.min(Math.max(0, fromIndex + this.length), findIndex);
​
  for (let index = actualFromIndex; index < this.length; index++) {
    if (this[index] === searchElement) {
      findIndex = index;
    }
  }
  return findIndex;
};

slice

意义: 提取一个数组中的某些元素,并返回一个包含被提取元素的新数组,具体提取哪些元素,由start和end参数决定。

slice(start,end)意义
start(可选)提取起始处的索引(默认为0)
end(可选)提取终止处的索引(默认为0)

实际应用:

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

重写API:

Array.prototype.mySlice = function (start = 0, end) {
  let sliceArr = [];
  if (start > this.length) return sliceArr;
  let startIndex, endIndex;
  startIndex = Math.min(Math.max(0, start + this.length), start);
  endIndex = Math.min(Math.max(0, end + this.length), end);
  if (endIndex > this.length || !endIndex) endIndex = this.length;
  if (startIndex > endIndex) return sliceArr;
  for (let index = startIndex; index < endIndex; index++) {
    sliceArr[sliceArr.length] = this[index];
  }
  return sliceArr;
};

concat

意义: 用于合并两个或多个数组,此方法不会改变原数组,而是返回一个新数组。

实际应用:

const arr = [1,2,3];
const concatArr1 = arr.concat([4],[5]); // [1,2,3,4,5]
const concatArr2 = arr.concat(4,5); // [1,2,3,4,5]

重写API:

Array.prototype.myConcat = function (...args) {
  const newArr = [...this];
  args.forEach((item) => {
    if (Array.isArray(item)) {
      newArr.push(...item);
    } else {
      newArr.push(item);
    }
  });
  return newArr;
};

map

意义: 此方法将返回一个新数组,新数组中的元素由原数组中的每一个元素调用map提供的函数参数后得到的返回值组成的。

实际应用:

const arr = [1,2,3,4,5];
const mapArr = arr.map((item) => item + 1);
console.log(mapArr);// [2,3,4,5,6]

重写API:

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

filter

意义: 过滤数组中的某些元素,根据所给的筛选方法,对原数组中的每一个元素进行筛选,并返回一个由筛选通过的元素组成的新数组。

实际应用:

const arr = [1,2,3,4,5];
const filterArr = arr.filter((item) => item % 2 === 0);
console.log(filterArr);// [2,4]

重写API:

Array.prototype.myFilter = function (callback) {
  const res = [];
  for (let index = 0; i < this.length; index++) {
    callback(this[index], index, this) && res.push(this[index]);
  }
  return res;
};

reduce

意义: 对数组中的每一个元素按序执行一个提供的reducer函数,每一次运行reducer会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。

实际应用:

const arr = [1,2,3,4,5];
const sum = arr.reduce((acc, current) => acc + current, 0);
console.log(sum);// 15

重写API:

Array.prototype.myReduce = function (callback, ...arg) {
  let pre,
    start = 0;
  if (arg.length) {
    pre = arg[0];
  } else {
    pre = this[0];
    start = 1;
  }
  for (let index = start; index < this.length; index++) {
    pre = callback(pre, this[index], index, this);
  }
  return pre;
};

forEach

意义: 对数组中的每一个元素执行一次给定的函数,返回值为undefined。

实际应用:

const arr = [1,2,3];
const callbackFn = (element, index) => console.log(element, index);
arr.forEach(callbackFn);
// 1 0
// 2 1
// 3 2

重写API:

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

join

意义: 将一个数组中的所有元素连接成一个字符串,并返回这个字符串。

实际应用:

const arr = [1,2,3,4,5];
const joinArraryStr = arr.join('-');
console.log(joinArrayStr); // "1-2-3-4-5"

重写API:

Array.prototype.myJoin = function (str = ",") {
  let resultStr = "";
  for (let index = 0; index < this.length; index++) {
    resultStr += `${this[i]}${str}`;
  }
  return resultStr.slice(0, resultStr.length - 1);
};

flat

意义: 根据指定的深度递归地将所有子数组元素都拼接到新的数组中,并返回拼接后的新数组。

实际应用:

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

重写API:

Array.prototype.myFlat = function (dep = 1) {
  return this.reduce((acc, val) => {
    return acc.concat(
      Array.isArray(val) && dep > 0
        ? val.myFlat(dep - 1)
        : Array.isArray(val)
        ? [val]
        : val
    );
  }, []);
};