[收藏系列] 无聊de时光拿来把常见的数组方法重写一遍,它不香吗?

870 阅读11分钟

push - 影响原数组

定义:
将一个或多个元素添加到数组的末尾,并返回该数组的新长度。(会影响原数组)

语法:

arr.push(element1, ..., elementN)

参数:

  • elementN:被添加到数组末尾的元素。

返回值:
返回该数组的新长度。

实现过程:

Array.prototype._push = function () {
  var length = this.length;
  for (var i = 0; i < arguments.length; i++) {
    this[length + i] = arguments[i];
  }
  return this.length;
}

pop - 影响原数组

定义:
从数组中删除最后一个元素,并返回该元素的值。(会影响原数组)

语法:

arr.pop()

参数:

返回值:
返回从数组中删除的元素,如果数组为空则返回 undefined

实现过程:

Array.prototype._pop = function () {
  if (!this.length) return;
  var lastItem = this[this.length - 1];
  this.length = this.length - 1;
  return lastItem;
}

unshift - 影响原数组

定义:
将一个或多个元素添加到数组的开头,并返回该数组的新长度。(会影响原数组)

语法:

arr.unshift(element1, ..., elementN)

参数:

  • elementN:要添加到数组开头的元素或多个元素。

返回值:
返回该数组的新长度。

实现过程:

Array.prototype._unshift = function () {
  var len = arguments.length; // 添加的元素个数
  var loopNum = this.length + len - 1; // 总循环次数
  // 倒循环 
  for (var i = loopNum; i >= 0; i--) {
    // 如果 循环剩下的次数 小于 添加的元素个数 则直接把添加的元素添加进原数组
    if (i >= len) {
      this[i] = this[i - len];
    } else {
      this[i] = arguments[i];
    }
  }
  return this.length;
}

解析过程:
我们知道添加之后的 新数组的长度 = 原数组的长度 + 添加的元素个数。我们可以直接倒序循环这个新数组的长度,从新数组后面先排原数组的元素再排添加的元素,但原数组的元素要倒序取。当 循环剩下的次数 小于 添加的元素个数 时,说明原数组的元素都排好在新数组里面了,下面可以按添加的元素了。

shift - 影响原数组

定义:
从数组中删除第一个元素,并返回该元素的值。(会影响原数组)

语法:

arr.shift()

参数:

返回值:
返回从数组中删除的元素,如果数组为空则返回 undefined

实现过程:

Array.prototype._shift = function () {
  if (!this.length) return;
  var firstItem = this[0];
  for (var i = 1; i < this.length; i++) {
    this[i - 1] = this[i];
  }
  this.length = this.length - 1;
  return firstItem;
}

reverse - 影响原数组

定义:
将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。(会影响原数组)

语法:

arr.reverse()

参数:

返回值:
返回颠倒后的数组。

实现过程:

Array.prototype._reverse = function () {
  var temp = undefined;
  var loopNum = parseInt(this.length / 2);
  for (var i = 0; i < loopNum; i++) {
    temp = this[i];
    this[i] = this[this.length - 1 - i];
    this[this.length - 1 - i] = temp;
  }
  return this;
}

splice - 影响原数组

定义:
通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。(会影响原数组)

语法:

array.splice(start[, deleteCount[, item1[, item2[, ...]]]])

参数:

  • start:指定修改的开始位置(从0计数)。如果超出了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于array.length-n);如果负数的绝对值大于数组的长度,则表示开始位置为第0位。

  • deleteCount(可选):整数,表示要移除的数组元素的个数。

    如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。

    如果 deleteCount 被省略了,或者它的值大于等于array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。

    如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。

  • item1, item2, ...(可选):要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。

返回值:
返回被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。

实现过程:

Array.prototype._splice = function (startIndex, deleteCount, ...addElements) {
  let argumentsLen = arguments.length;
  let array = Object(this);
  let len = array.length;
  let deleteArr = new Array(deleteCount);

  startIndex = computeStartIndex(startIndex, len);
  deleteCount = computeDeleteCount(startIndex, len, deleteCount, argumentsLen);

  // 判断 sealed 对象和 frozen 对象, 即 密封对象 和 冻结对象
  if (Object.isSealed(array) && deleteCount !== addElements.length) {
    throw new TypeError('the object is a sealed object!')
  } else if (Object.isFrozen(array) && (deleteCount > 0 || addElements.length > 0)) {
    throw new TypeError('the object is a frozen object!')
  }

  // 拷贝删除的元素
  sliceDeleteElements(array, startIndex, deleteCount, deleteArr);
  // 移动删除元素后面的元素
  movePostElements(array, startIndex, len, deleteCount, addElements);

  // 插入新元素
  for (let i = 0; i < addElements.length; i++) {
    array[startIndex + i] = addElements[i];
  }

  array.length = len - deleteCount + addElements.length;

  return deleteArr;
}

解析过程:
这个方法可以说是数组方法里面最复杂的方法之一了,具体详情可以参考这篇文章,点我,看完基本都能懂,写得非常nice。

join

定义:
将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项,那么将返回该项而不使用分隔符。

语法:

arr.join([separator])

参数:

  • separator(可选):指定一个字符串来分隔数组的每个元素。默认会把分割器转换成字符串。如果不填或者为 undefined ,则默认用逗号来分隔。如果为空字符串(""),则所有元素之间都没有任何字符。

返回值:
返回一个所有数组元素连接的字符串。如果 arr.length 为 0,则返回空字符串。

实现过程:

Array.prototype._join = function(separator) {
  if(this.length === 0) return '';
  if(separator === undefined) separator = ',';
  var resultStr = this[0] + '';
  for(var i = 1;i<this.length;i++) {
    resultStr += separator + this[i];
  }
  return resultStr;
}

concat

定义:
用于合并两个或多个数组,返回一个新数组。

语法:

var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])

参数:

  • valueN:数组和/或值,将被合并到一个新的数组中。如果没有参数,则 concat 会返回调用此方法的现存数组的一个浅拷贝。

返回值:
返回一个新数组

实现过程:

Array.prototype._concat = function () {
  var result = JSON.parse(JSON.stringify(this));
  for (var i = 0; i < arguments.length; i++) {
    var item = arguments[i];
    if (Array.isArray(item)) { // 只会取一层元素, 深层嵌套的直接浅拷贝
      for (var j = 0; j < item.length; j++) {
        result[result.length] = item[j];
      }
    } else {
      result[result.length] = arguments[i];
    }
  }
  return result;
}

indexOf

定义:
返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回 -1 。

语法:

arr.indexOf(searchElement[, fromIndex])

参数:

  • searchElement:要查找的元素。
  • fromIndex(可选):开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找 ,以此类推。 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0.

返回值:
返回首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1 。

实现过程:

Array.prototype._indexOf = function (searchElement, fromIndex) {
  var index = -1;
  if (!fromIndex) fromIndex = 0; // fromIndex: 0 || "" || null || undefined
  if (typeof fromIndex !== 'number' && typeof fromIndex !== 'string') fromIndex = 0; // 不是数字或者字符串的都当没传
  if (typeof fromIndex === 'string' && typeof parseInt(fromIndex) !== 'number') fromIndex = 0; // 普通字符串直接忽略, 允许字符串数字
  if (fromIndex < 0) fromIndex = this.length - (fromIndex * -1);
  for (var i = parseInt(fromIndex); i < this.length; i++) {
    if (searchElement === this[i]) return i;
  }
  return index;
}

解析过程:
这个方法主要比较麻烦的地方有两点,第一就是 fromIndex 类型的判断,第二就是 fromIndex 为负值的情况。给大家画个图结合个小例子应该更好理解一点。

image.jpg

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

像上面的例子中,arr.indexOf(2, -1); 它找到 -1 的位置然后往查找的方向查找,也就是向右查找,发现只剩下一个 5 了,所以没有找到 2 返回了 -1; 而 arr.indexOf(2, -1); 它找到 -2 的位置也向右查找,发现了 2 和 5,2 符合目标,所以就返回了 2 的索引值 4。

reduce

定义:
对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回。

语法:

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

参数:

  • callback:执行数组中每个值 (如果没有提供 initialValue则第一个值除外)的函数,包含四个参数:
    • accumulator:累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
    • currentValue:数组中正在处理的元素。
    • index(可选):数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。
    • array(可选):调用reduce()的数组。
  • initialValue(可选):作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。

返回值:
函数累计处理的结果。

实现过程:

Array.prototype._reduce = function (cb, initialValue) {
  if (typeof cb !== 'function') throw new TypeError(cb + ' is not a function');
  if (this.length === 0 && initialValue === undefined) throw new TypeError('Reduce of empty array with no initial value');
  var total = 0;
  var index = 0;
  if (initialValue !== undefined) {
    total = initialValue;
  } else {
    total = this[0];
    index = 1;
  }
  for (var i = index; i < this.length; i++) {
    total = cb(total, this[i], i, this);
  }
  return total;
}

forEach

定义:
对数组的每个元素执行一次给定的函数。

语法:

arr.forEach(callback(currentValue [, index [, array]])[, thisArg])

参数:

  • callback:
    • currentValue:当前遍历到的元素。
    • index(可选):当前遍历到的索引。
    • array(可选):数组本身。
  • thisArg(可选):执行 callback 回调函数时,回调函数内部的 this 值。

返回值:
返回 undefined

实现过程:

Array.prototype._forEach = function (cb, thisArg) {
  if (typeof cb !== 'function') throw new TypeError(cb + ' is not a function');
  for (var i = 0; i < this.length; i++) {
    cb.call(thisArg, this[i], i, this);
  }
}

map

定义:
创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。

语法:

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])

参数:

  • callback:
    • currentValue:当前遍历到的元素。
    • index(可选):当前遍历到的索引。
    • array(可选):数组本身。
  • thisArg(可选):执行 callback 回调函数时,回调函数内部的 this 值。

返回值:
返回一个由原数组每个元素执行回调函数的结果组成的新数组。

实现过程:

Array.prototype._map = function (cb, thisArg) {
  if (typeof cb !== 'function') throw new TypeError(cb + ' is not a function');
  var result = [];
  for (var i = 0; i < this.length; i++) {
    result.push(cb.call(thisArg, this[i], i, this));
  }
  return result;
}

filter

定义:
创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。

语法:

var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])

参数:

  • callback:
    • element:当前遍历到的元素。
    • index(可选):当前遍历到的索引。
    • array(可选):数组本身。
  • thisArg(可选):执行 callback 回调函数时,回调函数内部的 this 值。

返回值:
返回一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。

实现过程:

Array.prototype._filter = function (cb, thisArg) {
  if (typeof cb !== 'function') throw new TypeError(cb + ' is not a function');
  var result = [];
  for (var i = 0; i < this.length; i++) {
    if (cb.call(thisArg, this[i], i, this)) {
      result.push(this[i]);
    }
  }
  return result;
}

some

定义:
测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的。如果用一个空数组进行测试,在任何情况下它返回的都是false。

语法:

arr.some(callback(element[, index[, array]])[, thisArg])

参数:

  • callback:
    • element:当前遍历到的元素。
    • index(可选):当前遍历到的索引。
    • array(可选):数组本身。
  • thisArg(可选):执行 callback 回调函数时,回调函数内部的 this 值。

返回值:
如果数组中有一项通过测试函数,则返回 true,否则返回 false

实现过程:

Array.prototype._some = function (cb, thisArg) {
  if (typeof cb !== 'function') throw new TypeError(cb + ' is not a function');
  for (var i = 0; i < this.length; i++) {
    if (cb.call(thisArg, this[i], i, this)) return true;
  }
  return false;
}

every

定义:
测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。若收到一个空数组,此方法在一切情况下都会返回 true。

语法:

arr.every(callback(element[, index[, array]])[, thisArg])

参数:

  • callback:
    • element:当前遍历到的元素。
    • index(可选):当前遍历到的索引。
    • array(可选):数组本身。
  • thisArg(可选):执行 callback 回调函数时,回调函数内部的 this 值。

返回值:
如果全部元素都通过测试函数,则返回 true,否则返回 false

实现过程:

Array.prototype._every = function (cb, thisArg) {
  if (typeof cb !== 'function') throw new TypeError(cb + ' is not a function');
  for (var i = 0; i < this.length; i++) {
    if (!cb.call(thisArg, this[i], i, this)) return false;
  }
  return true;
}

find

定义:
返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。

语法:

arr.find(callback[, thisArg])

参数:

  • callback:
    • element:当前遍历到的元素。
    • index(可选):当前遍历到的索引。
    • array(可选):数组本身。
  • thisArg(可选):执行 callback 回调函数时,回调函数内部的 this 值。

返回值:
返回数组中第一个满足所提供测试函数的元素的值,否则返回 undefined

实现过程:

Array.prototype._find = function (cb, thisArg) {
  if (typeof cb !== 'function') throw new TypeError(cb + ' is not a function');
  for (var i = 0; i < this.length; i++) {
    if (cb.call(thisArg, this[i], i, this)) return this[i];
  }
  return;
}

findIndex

定义:
返回数组中满足提供的测试函数的第一个元素的索引,若没有找到对应元素则返回 -1 。

语法:

arr.findIndex(callback[, thisArg])

参数:

  • callback:
    • element:当前遍历到的元素。
    • index(可选):当前遍历到的索引。
    • array(可选):数组本身。
  • thisArg(可选):执行 callback 回调函数时,回调函数内部的 this 值。

返回值:
返回数组中第一个通过回调函数测试的元素的索引,否则,则返回 -1 。

实现过程:

Array.prototype._findIndex = function (cb, thisArg) {
  if (typeof cb !== 'function') throw new TypeError(cb + ' is not a function');
  for (var i = 0; i < this.length; i++) {
    if (cb.call(thisArg, this[i], i, this)) return i;
  }
  return -1;
}



至此,本篇文章就写完啦,撒花撒花。

image.png

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。