阅读 155

模拟 javaScript Array 原型上的方法

Array.prototype.push

let arr = [1,2,3,4,5]
let arrLike = {0:1,1:2,length:2}
let obj = {}
/**
 * Array.prototype.push(element1,...,elementN)
 * 向数组末尾添加N个元素并且返回数组长度
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/push
 * push 方法有意具有通用性。该方法和 call() 或 apply() 一起使用时,
 * 可应用在类似数组的对象上。push 方法根据 length 属性来决定从哪里开始插入给定的值。
 * 如果 length 不能被转成一个数值,则插入的元素索引为 0,
 * 包括 length 不存在时。当 length 不存在时,将会创建它。
 */
Array.prototype.myPush = function() {
  var length = this.length ? this.length : (this.length = 0) && 0
  var index = 0
  while (index < arguments.length) {
    this[length] = arguments[index]
    ++index
    ++length
  }
  this.length = length
  return this.length
}
arr.myPush(1,2,3,4)
Array.prototype.myPush.call(obj,1,2,{}) 
Array.prototype.myPush.call(arrLike,3,4,{})
console.log(arr)
console.log(obj)
console.log(arrLike)
复制代码

Array.prototype.pop

/**
 * Array.prototype.pop()
 * 向数组末尾删除元素并且返回该元素
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/pop
 * 从数组中删除的元素(当数组为空时返回undefined)
 * pop 方法有意具有通用性。该方法和 call() 或 apply() 一起使用时,可应用在类似数组的对象上。
 * pop方法根据 length属性来确定最后一个元素的位置。
 * 如果不包含length属性或length属性不能被转成一个数值,会将length置为0,并返回undefined。
 */
Array.prototype.myPop = function () {
  var length = this.length ? this.length : (this.length = 0) && 0
  if(length === 0)return
  var last = this[length-1]
  delete this[length - 1]
  --this.length
  return last
}
console.log(Array.prototype.myPop.call(arr))
console.log(Array.prototype.myPop.call(arrLike))
console.log(arr,obj,arrLike)
复制代码

Array.prototype.shift

/** 
 * Array.prototype.shift()
 * 向数组索引为0的位置删除元素并且返回该元素
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
 * shift 方法移除索引为 0 的元素(即第一个元素),并返回被移除的元素,
 * 其他元素的索引值随之减 1。如果 length 属性的值为 0 (长度为 0),则返回 undefined。
 * shift 方法并不局限于数组:这个方法能够通过 call 或 apply 方法作用于类似数组的对象上。
 * 但是对于没有 length 属性(从0开始的一系列连续的数字属性的最后一个)的对象,调用该方法可能没有任何意义。
 * 
*/ 
Array.prototype.myShift = function(){
  var length = this.length ? this.length : 0
  if(length === 0)return
  var first = this[0]
  var index = 1
  while (index < this.length) {
    this[index - 1] = this[index]
    index ++
  }
  delete this[length - 1]
  --this.length
  return first
}
console.log(arr.myShift())
console.log(Array.prototype.myShift.call(obj))
console.log(Array.prototype.myShift.call(arrLike))
复制代码

Array.prototype.unshift

/** 
 * Array.prototype.unshift(element1,...,elementN)
 * 向数组索引为0的位置插入N个元素并且返回该数组长度
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
 * unshift 方法会在调用它的类数组对象的开始位置插入给定的参数。
 * unshift 特意被设计成具有通用性;这个方法能够通过 call 或 apply 方法作用于类数组对象上。
 * 不过对于没有 length 属性(代表从0开始的一系列连续的数字属性的最后一个)的对象,调用该方法可能没有任何意义。
 * 
*/ 
Array.prototype.myUnshift = function() {
  if(!this.length)return
  var length = this.length
  var arglength = arguments.length
  this.length = length + arglength
  var index = length - 1
  while (index >= 0) {
    this[index + arglength] = this[index]
    --index
  }
  index = arglength - 1
  while (index >= 0) {
    this[index] = arguments[index]
    --index
  }
  return this.length
}
console.log(arr.myUnshift(0,1,3,4,5))
console.log(Array.prototype.myUnshift.call(obj,0,1,3,4,5))
console.log(Array.prototype.myUnshift.call(arrLike,0,1,3,4,5))
复制代码

Array.prototype.slice

/** 
 * Array.prototype.slice(begin,end)
 * 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
 * slice 不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。原数组的元素会按照下述规则拷贝:
 * 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。
 * 两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
 * 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。
 * 在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。
 * 
*/ 
Array.prototype.mySlice = function(begin,end) {
  if(!this.length)return
  var arr = []
  var index = 0
  begin = typeof begin === 'number' ? 
        begin < 0 ? this.length + begin 
        : begin 
        : 0
  end =  typeof end === 'number' ? 
        end > this.length ? this.length 
        : end 
        : this.length
  while (begin < end) {
    arr[index] = this[begin]
    begin++
    index++
  }
  return arr
}
console.log(arr.mySlice(-4))
console.log(Array.prototype.mySlice.call(obj,0,2))
console.log(Array.prototype.mySlice.call(arrLike,0,1))
复制代码

Array.prototype.concat

/** 
 * Array.prototype.concat(item1,...,itemN)
 * 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
 * concat方法创建一个新的数组,它由被调用的对象中的元素组成,
 * 每个参数的顺序依次是该参数的元素(如果参数是数组)或参数本身(如果参数不是数组)。它不会递归到嵌套数组参数中。
 * concat方法不会改变this或任何作为参数提供的数组,而是返回一个浅拷贝
 * 
*/ 
Array.prototype.myConcat = function() {
  if(!arguments.length)return
  var arr = []
  var index = 0
  while (index<this.length) {
    arr[index] = this[index]
    ++index
  }
  var i = 0
  while (i < arguments.length) {
    var el = arguments[i]
    if(el instanceof Array) {
      if(el.length){
        var j = 0
        while (j < el.length) {
          arr[index] = el[j]
          ++index
          ++j
        }
      }
    }else{
      arr[index] = el
      ++index
    }
    ++i
  }
  return arr
}
console.log(arr.myConcat(1,[2,3],[4,[5,[6],[7]]]))
console.log(arr.concat(1,[2,3],[4,[5,[6],[7]]]))
console.log(arr)
复制代码

Array.prototype.splice

/** 
 * Array.prototype.splice(start,deleteCount,item1,...,itemN)
 * 方法通过删除或替换现有元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
 * 如果添加进数组的元素个数不等于被删除的元素个数,数组的长度会发生相应的改变。
 * 
*/ 
Array.prototype.mySplice = function(start,deleteCount) {
  if(!this.length)return
  var arr = []
  /**
   * 如果超出了数组的长度,则从数组末尾开始添加内容;
   * 如果是负值,则表示从数组末位开始的第几位(从-1计数);
   * 如果负数的绝对值大于数组的长度,则表示开始位置为第0位。
   */
  start = typeof start === 'number' ?
          start > this.length ? this.length 
        : start < 0 ? this.length + start < 0 ? 0 
        : this.length + start 
        : start 
        : 0
  /** 
   * 如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。
   * 如果 deleteCount 被省略,则其相当于 array.length - start。
   * 如果 deleteCount 被省略了,或者它的值大于等于array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。
   * 如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。
  */
  deleteCount = typeof deleteCount === 'number' ? 
              deleteCount < 0 ? 0 
            : deleteCount > this.length - start ? this.length - start 
            : deleteCount : deleteCount === undefined ? this.length - start 
            : 0
  //取出除去前两个参数之后的剩余参数
  var args = arguments.length > 2 ? Array.prototype.mySlice.call(arguments,2) : []
  var argLength = args.length

  //记录一下开始位置
  var oIndex = start
  //需要新增或者缩减的数目
  var moveLength = argLength - deleteCount
  //需要删除到指定的下标
  var delIndex = deleteCount + start
  //新增到指定的下表
  var addIndex = argLength + start
  var index = 0
  //删除 [...start, ... ,delIndex,...]
  while (start < delIndex) {
    arr[index] = this[start]
    this[start] = null
    ++start
    ++index
  }
  if(moveLength > 0){
    //数组不足以插入的时候,开辟新的位置
    var i = this.length - 1
    this.length += moveLength
    while (i >= oIndex) {
      this[i+moveLength] = this[i]
      --i
    }
  }else{
    //插入后还有剩余,需要回缩空间
    var i = this.length
    if(start < this.length){
      while (start < i) {
        this[start+moveLength] = this[start]
        ++start
      }
    }
    this.length += moveLength
  }
  var i = 0
  // 插入新的 item1...itemN
  while (oIndex < addIndex) {
    this[oIndex] = args[i]
    ++i
    ++oIndex
  }
  return arr
}
console.log(arrLike)
console.log(Array.prototype.mySplice.call(arrLike,1,1))
console.log(arrLike)
console.log(arr.mySplice())
console.log(arr)
复制代码

Array.prototype.reduce

/**
 * Array.prototype.reduce(callback,initialValue)
 * reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
 * callback 执行数组中每个值的函数,包含四个参数:
  * accumulator: 累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
  * currentValue: 数组中正在处理的元素。
  * currentIndex可选: 数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则为1。
  * array可选: 调用reduce()的数组
 * initialValue 可选
 * 作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
 */
Array.prototype.myReduce = function(callback){
  var arr = this.mySlice()
  var len = arr.length
  var index = 0
  var initialValue
  if(arguments.length >= 2){
    //如果有默认值取默认值
    initialValue = arguments[1] 
  }else{
    //如果没有默认值,取第一个有值的索引,处理稀疏数组,若第一项没有值的时候起始索引往后走
    while (index < len && !(arr[index] in arr)) {
      ++index
    }
    if(index >= len) return
    initialValue = arr[index++]
  }
  while (index < len) {
    if(arr[index] in arr){
      //值存在才走进来
      initialValue = callback.call(null, initialValue, arr[index], index, arr)
    }
    ++index
  }
  return initialValue
}
var sum = [0,1,2,3,4].myReduce(function(accumulator, currentValue, currentIndex, array){
  console.log(accumulator, currentValue, currentIndex, array)
  return accumulator + currentValue;
}); // 10
var sum = [, 1, ,3,,].myReduce(function(accumulator, currentValue, currentIndex, array){
  console.log(accumulator, currentValue, currentIndex, array)
  return accumulator + currentValue;
}); // 4
复制代码

Array.prototype.reduceRight

/**
 * Array.prototype.reduceRight(callback,initialValue)
 * reduceRight() 方法对数组中的每个元素执行一个由您提供的reducer函数(降序执行),将其结果汇总为单个返回值。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight
 * callback 执行数组中每个值的函数,包含四个参数:
  * accumulator: 累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
  * currentValue: 数组中正在处理的元素。
  * currentIndex可选: 数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则为1。
  * array可选: 调用reduceRight()的数组
 * initialValue 可选
 * 作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduceRight 将报错。
 */
Array.prototype.myReduceRight = function(callback){
  var arr = this.mySlice()
  var len = arr.length-1
  var index = len
  var initialValue
  if(arguments.length >= 2){
    //如果有默认值取默认值
    initialValue = arguments[1] 
  }else{
    //如果没有默认值,取第一个有值的索引,处理稀疏数组,若最后一项项没有值的时候起始索引往前走
    while (index >= 0 && !(arr[index] in arr)) {
      --index
    }
    if(index <= 0) return
    initialValue = arr[index--]
  }
  while (index >= 0) {
    if(arr[index] in arr){
      //值存在才走进来
      initialValue = callback.call(null, initialValue, arr[index], index, arr)
    }
    index--
  }
  return initialValue
}
var sum = [0,1,2,3,4].myReduceRight(function(accumulator, currentValue, currentIndex, array){
  console.log(accumulator, currentValue, currentIndex, array)
  return accumulator + currentValue;
},2); // 12
var sum = [, 1, ,3,,].myReduceRight(function(accumulator, currentValue, currentIndex, array){
  console.log(accumulator, currentValue, currentIndex, array)
  return accumulator + currentValue;
},2); // 6
复制代码

Array.prototype.forEach

/**
 * Array.prototype.forEach(callback,context)
 * forEach() 方法对数组的每个元素执行一次提供的函数。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
 * callback 生成新数组元素的函数,使用三个参数:
    * currentValue
      * callback 数组中正在处理的当前元素。
    * index可选
      * callback 数组中正在处理的当前元素的索引。
    * array可选
      * callback  map 方法被调用的数组。
  * thisArg可选
    * 执行 callback 函数时使用的this 值。
 */
Array.prototype.myForEach = function(callback){
  var len = this.length
  var index = 0
  var context = arguments[1] || this
  while (index < len) {
    callback.call(context, this[index], index, this)
    index++
  }
}
[1,2,3,4,5].forEach(function(current, index, arr) {
  console.log(current, index, arr, this.a)
},{a:1})
[1,2,3,4,5].myForEach(function(current, index, arr){
  console.log(current, index, arr, this.a)
},{a:1})
复制代码

Array.prototype.map

/**
 * Array.prototype.map(callback,context)
 * map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map
 * callback 生成新数组元素的函数,使用三个参数:
    * currentValue
      * callback 数组中正在处理的当前元素。
    * index可选
      * callback 数组中正在处理的当前元素的索引。
    * array可选
      * callback  map 方法被调用的数组。
  * thisArg可选
    * 执行 callback 函数时使用的this 值。
 */
Array.prototype.myMap = function(callback){
  var arr = []
  var len = this.length
  var index = 0
  var context = arguments[1] || this
  while (index < len) {
    arr.myPush(callback.call(context, this[index], index, this))
    index++
  }
  return arr
}
console.log([1,2,3,4,5].map(function(current, index, arr) {
  console.log(current, index, arr)
  return index + this.a
},{a:1})) //[1,2,3,4,5]
console.log([1,2,3,4,5].myMap(function(current, index, arr) {
  console.log(current, index, arr)
  return index + this.a
},{a:1})) //[1,2,3,4,5]
复制代码

Array.prototype.filter

/**
 * Array.prototype.filter(callback,context)
 * filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
 * callback 生成新数组元素的函数,使用三个参数:
    * currentValue
      * callback 数组中正在处理的当前元素。
    * index可选
      * callback 数组中正在处理的当前元素的索引。
    * array可选
      * callback  filter 方法被调用的数组。
  * thisArg可选
    * 执行 callback 函数时使用的this 值。
 */
Array.prototype.myFilter = function(callback){
  var arr = []
  var len = this.length
  var index = 0
  var context = arguments[1] || this
  while (index < len) {
    var el = this[index]
    callback.call(context, el, index, this) && arr.myPush(el)
    index++
  }
  return arr
}
console.log([1,2,3,4,5].filter(function(current, index, arr) {
  console.log(current, index, arr)
  return index > this.a
},{a:1}))
console.log([1,2,3,4,5].myFilter(function(current, index, arr) {
  console.log(current, index, arr)
  return index > this.a
},{a:1}))/**
 * Array.prototype.filter(callback,context)
 * filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
 * callback 生成新数组元素的函数,使用三个参数:
    * currentValue
      * callback 数组中正在处理的当前元素。
    * index可选
      * callback 数组中正在处理的当前元素的索引。
    * array可选
      * callback  filter 方法被调用的数组。
  * thisArg可选
    * 执行 callback 函数时使用的this 值。
 */
Array.prototype.myFilter = function(callback){
  var arr = []
  var len = this.length
  var index = 0
  var context = arguments[1] || this
  while (index < len) {
    var el = this[index]
    callback.call(context, el, index, this) && arr.myPush(el)
    index++
  }
  return arr
}
console.log([1,2,3,4,5].filter(function(current, index, arr) {
  console.log(current, index, arr)
  return index > this.a
},{a:1}))//[3,4,5]
console.log([1,2,3,4,5].myFilter(function(current, index, arr) {
  console.log(current, index, arr)
  return index > this.a
},{a:1}))//[3,4,5]
复制代码

Array.prototype.every

/**
 * Array.prototype.every(callback,context)
 * every() 方法测试数组的所有元素是否都通过了指定函数的测试。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/every
 * callback 生成新数组元素的函数,使用三个参数:
    * currentValue
      * callback 数组中正在处理的当前元素。
    * index可选
      * callback 数组中正在处理的当前元素的索引。
    * array可选
      * callback  every 方法被调用的数组。
  * thisArg可选
    * 执行 callback 函数时使用的this 值。
 */
Array.prototype.myEvery = function(callback){
  var every = true
  var len = this.length
  var index = 0
  var context = arguments[1] || this
  while (index < len) {
    if(!callback.call(context, this[index], index, this)) {
      every = false 
      break 
    }
    index++
  }
  return every
}
console.log([1,2,3,4,5].every(function(current, index, arr) {
  console.log(current, index, arr)
  return current > this.a
},{a:0})) // true
console.log([1,2,3,4,5].myEvery(function(current, index, arr) {
  console.log(current, index, arr)
  return current > this.a
},{a:0})) // true
复制代码

Array.prototype.some

/**
 * Array.prototype.some(callback,context)
 * some() 方法测试是否至少有一个元素通过由提供的函数实现的测试。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/some
 * callback 生成新数组元素的函数,使用三个参数:
    * currentValue
      * callback 数组中正在处理的当前元素。
    * index可选
      * callback 数组中正在处理的当前元素的索引。
    * array可选
      * callback  some 方法被调用的数组。
  * thisArg可选
    * 执行 callback 函数时使用的this 值。
 */
Array.prototype.mySome = function(callback){
  var every = false
  var len = this.length
  var index = 0
  var context = arguments[1] || this
  while (index < len) {
    if(callback.call(context, this[index], index, this)) {
      every = true 
      break 
    }
    index++
  }
  return every
}
console.log([1,2,3,4,5].some(function(current, index, arr) {
  console.log(current, index, arr)
  return current > this.a
},{a:10})) // false
console.log([1,2,3,4,5].mySome(function(current, index, arr) {
  console.log(current, index, arr)
  return current > this.a
},{a:10})) // false
复制代码

Array.prototype.find

/**
 * arr.find(callback[, thisArg]) 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/find
 * callback
  * 在数组每一项上执行的函数,接收 3 个参数:
 * element
  * 当前遍历到的元素。
 * index可选
  * 当前遍历到的索引。
 * array可选
  * 数组本身。
 * thisArg可选
  * 执行回调时用作this 的对象。
*/
Array.prototype.myFind = function(callback,context) {
  context = context || window
  var len = this.length
  var i = 0
  while (i < len) {
    if(callback.call(context,this[i],i,this))
      return this[i]
    i++
  }
  return undefined
}
console.log([4, 6, 8, 12].myFind(function(item) {
  return item + this.a > 10
},{a:5})); // 6
console.log([4, 6, 8, 12].find(function(item) {
  return item + this.a > 10
},{a:5})); // 6
复制代码

Array.prototype.findIndex

/**
 * arr.findIndex(callback[, thisArg]) 方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
 * callback
  * 在数组每一项上执行的函数,接收 3 个参数:
 * element
  * 当前遍历到的元素。
 * index可选
  * 当前遍历到的索引。
 * array可选
  * 数组本身。
 * thisArg可选
  * 执行回调时用作this 的对象。
*/
Array.prototype.myFindIndex = function(callback,context) {
  context = context || window
  var len = this.length
  var i = 0
  while (i < len) {
    if(callback.call(context,this[i],i,this))
      return i
    i++
  }
  return -1
}
console.log([4, 6, 8, 12].myFindIndex(function(item) {
  return item + this.a > 10
},{a:5})); // 1
console.log([4, 6, 8, 12].findIndex(function(item) {
  return item + this.a > 10
},{a:5})); // 1
复制代码

Array.prototype.join

/**
 * Array.prototype.join(separator)
 * join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/join
 * separator 
 * 指定一个字符串来分隔数组的每个元素。如果需要,将分隔符转换为字符串。
 * 如果省略(),数组元素用逗号分隔。默认为 ","。如果separator是空字符串(""),则所有元素之间都没有任何字符。
 */
Array.prototype.myJoin = function(separator){
  var separator = typeof separator === 'string' ? separator : ','
  var len = this.length
  var str = ''
  if(!len) return str
  var index = 1
  str = this[0]  ? this[0].toString() : '' 
  while (index < len) {
    str += separator + (this[index] ? this[index].toString() : '')
    index++
  }
  return str
}
console.log([1,null,,{},[],/2/].myJoin(',') === [1,null,,{},[],/2/].join(',')) //true

复制代码

Array.prototype.reverse

/**
 * Array.prototype.reverse()
 * reverse()  方法将数组中元素的位置颠倒,并返回该数组。该方法会改变原数组。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
 * 方法颠倒数组中元素的位置,并返回该数组的引用。
 */
Array.prototype.myReverse = function(){
  if(!this.length) return this
  var len = this.length - 1
  var index = 0
  var mid = Math.floor(this.length / 2)
  while (index < mid) {
    var lastIndex = len-index
    var tem = this[index]
    var last = this[lastIndex]
    var indexEmpty = !(index in this)
    var lastIndexEmpty = !(lastIndex in this)

    if(lastIndexEmpty){
      delete this[index]
    }else{
      this[index] = last
    }
    if(indexEmpty){
      delete this[lastIndex]
    }else{
      this[len-index] = tem
    }
    index++
  }
  return this
}
var arr1 = [1,2,,3,,4,,5]
var arr2 = [1,2,,3,,4,,5]
arr1.myReverse()
console.log(arr1) //[5, empty, 4, empty, 3, empty, 2, 1]
arr2.reverse()
console.log(arr2) //[5, empty, 4, empty, 3, empty, 2, 1]
复制代码

Array.prototype.sort

/**
 * sort() 方法用原地算法对数组的元素进行排序,并返回数组。排序算法现在是稳定的。默认排序顺序是根据字符串Unicode码点。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
 * compareFunction 可选
 * 用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。
  * firstEl
  * 第一个用于比较的元素。
  * secondEl
  * 第二个用于比较的元素。
 */
/**
 * 快排
 * @param {*} arr 待排序数组
 * @param {*} low 起点
 * @param {*} high 终点
 * @param {*} cb 比较函数
 */
function quickSort(arr,low,high,cb) {
  if(low<high){
    var mid = partition(arr,low,high,cb)
    quickSort(arr,low,mid-1,cb)
    quickSort(arr,mid+1,high,cb)
  }
  return arr
}
/**
 * 划分函数 
 */
function partition(arr,low,high,cb) {
  var poivt = arr[low]
  while (low<high) {
    while (low<high && cb(arr[high],poivt) >= 0 ) {
      high--
    }
    arr[low] = arr[high]
    while (low<high && cb(arr[low],poivt) <= 0 ) {
      low++
    }
    arr[high] = arr[low]
  }
  arr[low] = poivt
  return low
}
Array.prototype.mySort = function(cb) {
  return quickSort(this,0,this.length-1,cb)
}


var arr1 = [3,5,5,-1,65,6,41,2,51,11,52,8]
var arr2 = [3,5,5,-1,65,6,41,2,51,11,52,8]
function fcb(a,b) {
  return a - b
}
console.log(arr1.mySort(fcb)) //[-1, 2, 3, 5, 5, 6, 8, 11, 41, 51, 52, 65]
console.log(arr2.sort(fcb)) //[-1, 2, 3, 5, 5, 6, 8, 11, 41, 51, 52, 65]
复制代码

Array.prototype.indexOf

/**
 * indexOf() 方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
 * searchElement
  * 要查找的元素
 * fromIndex
  * 开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。
  * 如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,
  * 即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找 ,以此类推。
  * 注意:
  * 如果参数中提供的索引值是一个负值,并不改变其查找顺序,
  * 查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0.
 */
Array.prototype.myIndexOf = function(search,fromIndex){
  fromIndex = fromIndex ? typeof fromIndex === 'number' ? fromIndex 
                : typeof fromIndex === 'string' ? (fromIndex-=0) && fromIndex === fromIndex ? fromIndex 
                : 0 : 0 : 0
  var index = -1
  var len = this.length
  var i = fromIndex < 0 ? len + fromIndex : fromIndex
  while (i < len) {
    if(search == this[i]){
      index = i
      break
    }
    i++
  }
  return index
}
console.log(arr1.myIndexOf(5,{}) == arr1.indexOf(5,{})) //true
console.log(arr1.myIndexOf(5,[]) == arr1.indexOf(5,[])) //true
console.log(arr1.myIndexOf(5,[1]) == arr1.indexOf(5,[1])) //true
console.log(arr1.myIndexOf(5,'1') == arr1.indexOf(5,'1')) //true
console.log(arr1.myIndexOf(5,'1e') == arr1.indexOf(5,'1e')) //true
console.log(arr1.myIndexOf(5,true) == arr1.indexOf(5,true)) //true
console.log(arr1.myIndexOf(5,NaN) == arr1.indexOf(5,NaN)) //true
console.log(arr1.myIndexOf(5,-1) == arr1.indexOf(5,-1)) //true
console.log(arr1.myIndexOf(5,-5) == arr1.indexOf(5,-5)) //true
复制代码

Array.prototype.lastIndexOf

/**
 * lastIndexOf() 方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf
 * searchElement
  * 要查找的元素
 * fromIndex
  * 从此位置开始逆向查找。默认为数组的长度减 1,即整个数组都被查找。
  * 如果该值大于或等于数组的长度,则整个数组会被查找。
  * 如果为负值,将其视为从数组末尾向前的偏移。
  * 即使该值为负,数组仍然会被从后向前查找。
  * 如果该值为负时,其绝对值大于数组长度,则方法返回 -1,
  * 即数组不会被查找。
 */
Array.prototype.myLastIndexOf = function(search,fromIndex){
  fromIndex = fromIndex ? typeof fromIndex === 'number' ? fromIndex 
              : (fromIndex-=0) && fromIndex === fromIndex ? fromIndex 
              : 0 
              : 0
  var index = -1
  var i = fromIndex < 0 ? fromIndex + this.length > 0 ? fromIndex + this.length : 0 : fromIndex > this.length ? this.length : fromIndex
  while (i > 0) {
    if(search == this[i]){
      index = i
      break
    }
    i--
  }
  return index
}
console.log(arr1.myLastIndexOf(5,{}) == arr1.lastIndexOf(5,{})) //true
console.log(arr1.myLastIndexOf(5,[]) == arr1.lastIndexOf(5,[])) //true
console.log(arr1.myLastIndexOf(5,[1]) == arr1.lastIndexOf(5,[1])) //true
console.log(arr1.myLastIndexOf(5,'1') == arr1.lastIndexOf(5,'1')) //true
console.log(arr1.myLastIndexOf(5,'1e') == arr1.lastIndexOf(5,'1e')) //true
console.log(arr1.myLastIndexOf(5,true) == arr1.lastIndexOf(5,true)) //true
console.log(arr1.myLastIndexOf(5,NaN) == arr1.lastIndexOf(5,NaN)) //true
console.log(arr1.myLastIndexOf(5,-1) == arr1.lastIndexOf(5,-1)) //true
console.log(arr1.myLastIndexOf(5,-5) == arr1.lastIndexOf(5,-5)) //true
复制代码

Array.prototype.from

/**
 * Array.from(arrayLike[, mapFn[, thisArg]]) 从一个类似数组或可迭代对象中创建一个新的数组实例。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from
 * arrayLike
  * 想要转换成数组的伪数组对象或可迭代对象。
 * mapFn (可选参数)
  * 如果指定了该参数,新数组中的每个元素会执行该回调函数。
 * thisArg (可选参数)
  * 可选参数,执行回调函数 mapFn 时 this 对象。  
*/
Array.prototype.myFrom = function(arrayLike,mapFn,context) {
  context = context || window
  mapFn = mapFn || function(item){return item}
  var arr = []
  if(arrayLike.forEach){
    arrayLike.forEach((value)=>{
      arr.push(mapFn.call(context,value))
    })
  }else{
    var length = arrayLike.length
    var i = 0
    while (i<length) {
      arr.push(mapFn.call(context,arrayLike[i]))
      i++
    }
  }
  return arr
}
console.log(Array.prototype.myFrom(arrLike))
console.log(Array.prototype.myFrom(set))
console.log(Array.prototype.myFrom(map))
复制代码

Array.prototype.of

/**
 * Array.of(element0[, element1[, ...[, elementN]]]) 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/of
 * elementN
  * 任意个参数,将按顺序成为返回数组中的元素。  
*/
Array.prototype.myOf = function() {
  var len = arguments.length
  var arr =[]
  arr.length = len
  var i = 0
  while (i < len) {
    arr[i] = arguments[i]
    i++
  }
  return arr
  // return Array.prototype.mySlice.call(arguments)
  // return Array.prototype.myFrom.call(null,arguments)
}
console.log(Array.prototype.myOf(1,2,3))
console.log(Array.prototype.myOf(undefined))
console.log(Array.prototype.myOf(1))
复制代码

Array.prototype.copyWithin

/**
 * Array.copyWithin(target[, start[, end]]) 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,而不修改其大小。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin
 * target
  * 0 为基底的索引,复制序列到该位置。如果是负数,target 将从末尾开始计算。
  * 如果 target 大于等于 arr.length,将会不发生拷贝。如果 target 在 start 之后,复制的序列将被修改以符合 arr.length。
 * start
  * 0 为基底的索引,开始复制元素的起始位置。如果是负数,start 将从末尾开始计算。
  * 如果 start 被忽略,copyWithin 将会从0开始复制。
 * end
  * 0 为基底的索引,开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数, end 将从末尾开始计算。
  * 如果 end 被忽略,copyWithin 方法将会一直复制至数组结尾(默认为 arr.length)。  
*/
Array.prototype.myCopyWithin = function(target,start,end) {
  var len = this.length
  target = target < 0 ? Math.abs(target) > len ? len : len + target : target > len ? len : target
  start = typeof start === 'number' ? start < 0 ? Math.abs(start) > len ? len : len + start : start > len ? len : start : 0
  end = typeof end === 'number' ? end < 0 ? Math.abs(end) > len ? len : len + end : end > len ? len : end : len
  var oTarget = target
  var offset = end - start
  var arr = Array.prototype.mySlice.call(this)
  while (target < len && (target-oTarget) < offset && start < end) {
    if(!this[start])break
    this[target] = arr[start]
    start++
    target++
  }
  return this
}
console.log([1, 2, 3, 4, 5].myCopyWithin(-2)); // [1, 2, 3, 1, 2]
console.log([1, 2, 3, 4, 5].copyWithin(-2)); // [1, 2, 3, 1, 2]
console.log([1, 2, 3, 4, 5].myCopyWithin(0, 3)); // [4, 5, 3, 4, 5]
console.log([1, 2, 3, 4, 5].copyWithin(0, 3)); // [4, 5, 3, 4, 5]
console.log([1, 2, 3, 4, 5].myCopyWithin(0, 3, 4));// [4, 2, 3, 4, 2]
console.log([1, 2, 3, 4, 5].copyWithin(0, 3, 4));// [4, 5, 3, 4, 5]
console.log([1, 2, 3, 4, 5].myCopyWithin(-2, -3, -1)); // [1, 2, 3, 3, 4]
console.log([1, 2, 3, 4, 5].copyWithin(-2, -3, -1)); // [1, 2, 3, 3, 4]
console.log([1, 2, 3, 4, 5].myCopyWithin(3, 2, 4)); // [1, 2, 3, 3, 4]
console.log([1, 2, 3, 4, 5].copyWithin(3, 2, 4)); // [1, 2, 3, 3, 4]
console.log([].myCopyWithin.call({length: 5, 3: 1}, 0, 3)); // {0: 1, 3: 1, length: 5}
console.log([].copyWithin.call({length: 5, 3: 1}, 0, 3)); // {0: 1, 3: 1, length: 5}
console.log([].myCopyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4));// Int32Array [4, 2, 3, 4, 5]
console.log([].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4));// Int32Array [4, 2, 3, 4, 5]
复制代码

Array.prototype.fill

/**
 * Array.fill(callback[, thisArg]) 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
 * value
  * 用来填充数组元素的值。
 * start 可选
  * 起始索引,默认值为0。
 * end 可选
  * 终止索引,默认值为 this.length。
*/
Array.prototype.myFill = function(value,start,end) {
  var len = this.length
  start = typeof start === 'number' ? start < 0 ? Math.abs(start) > len ? len : len + start : start > len ? len : start : 0
  end = typeof end === 'number' ? end < 0 ? Math.abs(end) > len ? len : len + end : end > len ? len : end : len
  while (start < end) {
    this[start] = value
    start++
  }
  return this
}
console.log([1, 2, 3].myFill(4))               // [4, 4, 4]
console.log([1, 2, 3].myFill(4, 1))            // [1, 4, 4]
console.log([1, 2, 3].myFill(4, 1, 2))         // [1, 4, 3]
console.log([1, 2, 3].myFill(4, 1, 1))         // [1, 2, 3]
console.log([1, 2, 3].myFill(4, 3, 3))         // [1, 2, 3]
console.log([1, 2, 3].myFill(4, -3, -2))       // [4, 2, 3]
console.log([1, 2, 3].myFill(4, NaN, NaN))     // [1, 2, 3]
console.log([1, 2, 3].myFill(4, 3, 5))         // [1, 2, 3]
console.log(Array(3).myFill(4))                // [4, 4, 4]
console.log(Array.prototype.myFill.call({ length: 3 }, 4))  // {0: 4, 1: 4, 2: 4, length: 3}
复制代码

Array.prototype.includes

/**
 * Array.prototype.includes(valueToFind[, fromIndex])方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
 * valueToFind
  * 需要查找的元素值。
 * fromIndex 可选
  * 从fromIndex 索引处开始查找 valueToFind。默认为 0。
  * 如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。
*/
Array.prototype.myIncludes = function(valueToFind,findIndex) {
  var len = this.length
  findIndex = typeof findIndex === 'number' ? findIndex < 0 ? Math.abs(findIndex) > len ? len : len + findIndex : findIndex > len ? len : findIndex : 0
  while (findIndex < len) {
    var now = this[findIndex]
    if(valueToFind === now)return true
    if(valueToFind !== valueToFind && now !== now)return true
    findIndex++
  }
  return false
}
console.log([1, 2, 3].myIncludes(2))     // true
console.log([1, 2, 3].myIncludes(4))     // false
console.log([1, 2, 3].myIncludes(3, 3))  // false
console.log([1, 2, 3].myIncludes(3, -1)) // true
console.log([1, 2, NaN].myIncludes(NaN)) // true
复制代码

Array.prototype.keys

/**
 * Array.prototype.keys()方法返回一个包含数组中每个索引键的Array Iterator对象。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/keys
*/
Array.prototype.myKeys = function() {
  if(!typeof this === 'object')return 
  var arr = null
  var length = this.length
  if(!length){
    arr = []
    for (const key in this) {
      if (this.hasOwnProperty(key)) {
        arr.push(key)
      }
    }
  }
  var len = this.length || arr.length
  var nextIndex = 0
  return {
    //[Symbol.iterator]需要在对象添加此属性,才是一个可被 for...of 遍历的对象
    [Symbol.iterator]: function(){
      return {
        next:function(){
          return nextIndex < len ? {value: length ? nextIndex++ : arr[nextIndex++], done:false} : {done:true}
        }
      }
    }
  }
}
var a = ["a", "b", "c"].myKeys()
var b = Array.prototype.myKeys.call({0:1,1:2,length:2})
var c = Array.prototype.myKeys.call({a:1,b:2})
for (const value of a) {
  console.log(value) // 0 1 2
}
for (const value of b) {
  console.log(value) // 0 1
}
for (const value of c) {
  console.log(value) // a b
}
复制代码

Array.prototype.values

/**
 * Array.prototype.values()方法返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/values
*/
Array.prototype.myValues = function() {
  if(!typeof this === 'object')return 
  var arr = this
  if(!this.length){
    arr = []
    for (const key in this) {
      if (this.hasOwnProperty(key)) {
        arr.push(this[key])
      }
    }
  }
  var len = this.length || arr.length
  var nextIndex = 0
  return {
    //[Symbol.iterator]需要在对象添加此属性,才是一个可被 for...of 遍历的对象
    [Symbol.iterator]: function(){
      return {
        next:function(){
          return nextIndex < len ? {value: arr[nextIndex++], done:false} : {done:true}
        }
      }
    }
  }
}
var a = ["a", 'b', "c"].myValues()
var b = Array.prototype.myValues.call({0:1,1:2,length:2})
var c = Array.prototype.myValues.call({a:1,b:2})
for (const value of a) {
  console.log(value) // a b c
}
for (const value of b) {
  console.log(value) // 1 2
}
for (const value of c) {
  console.log(value) // 1 2
}
复制代码

Array.prototype.entries

/**
 * Array.prototype.entries()方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/entries
*/
Array.prototype.myEntries = function() {
  if(!typeof this === 'object')return 
  var arr = this
  var len = this.length || arr.length
  var nextIndex = 0
  return {
    //[Symbol.iterator]需要在对象添加此属性,才是一个可被 for...of 遍历的对象
    [Symbol.iterator]: function(){
      return {
        next:function(){
          return nextIndex < len ? {value:[nextIndex,arr[nextIndex++]], done:false} : {done:true}
        }
      }
    }
  }
}
var a = ["a", 'b', "c"].myEntries()
var b = Array.prototype.myEntries.call({0:1,1:2,length:2})
for (const value of a) {
  console.log(value) // [0,"a"]  [0, "b"]  [0, "c"]
}
for (const value of b) {
  console.log(value) // [0, 1]  [0, 2]
}
复制代码