# 内置对象-数组篇

366 阅读18分钟

数组属一种有序属性的集合 Array 是用于构造数组的全局对象,类似于其他语言的列表

Array.length

length 是 Array 实例的属性

  1. 数组的长度范围是 0 - 2^32 -1
  2. 可以设置属性的 length 的属性值来截断数组,当然也可以通过 length 来拓展数组
length 属性的特性
writabletrue
enumerablefalse
configurablefalse

Array.prototype.iterator

iterator 属性和 Array.prototype.values 属性的初始值是一个函数,默认的返回值与 values 相同

  1. 使用for of 可以进行遍历
  2. 另一种迭代方式是使用迭代器对象中的 next 方法
生成器 or 迭代器

什么是迭代?

  1. 简单的来说我们希望数组的遍历时可控的(可停止,可继续的);
  2. 遍历是一次又一次迭代的实现

什么是生成器

  1. Javascript 中的生成器是在声明函数前使用 * 例如 function *generator () {}
  2. 因此箭头函数不能够成为生成器函数
  3. 生成器函数中有 yield 方法来控制迭代
  4. 生成器函数需返迭代器对象迭代器对象中需有 next 方法
// 生成器迭代器
function* generator() {
  yield 'a'
  yield 'b'
  return 'c'
}
var iterator = generator()
iterator.next()
// 实现简单的生成器迭代器
function generator(arr) {
  var iteratorIdx = 0

  return {
    next() {
      return {
        done: iteratorIdx >= arr.length ? true : false,
        value: arr[iteratorIdx++],
      }
    },
  }
}
var iterator = generator(arr)

Array.prototype.concat

用于合并两个或者多个数组,此方法不会改变原数组返回一个新数组,浅拷贝

  • concat 一个或多个参数
  • 如果 concat 不传入参数则会浅拷贝当前数组
  • concat方法创建一个新的数组,他由被调用的对象中的元素组成,每个参数的顺序依次是按照参数的元素(如果参数是数组),或本身(如果参数不是数组)
  • concat方法会返回一个浅拷贝,他包含与原始数组相结合相同元素的副本
// 使用方法 
var alpha = ['a', 'b', 'c'];
var numeric = [1, 2, 3];

alpha.concat(numeric);
// result in ['a', 'b', 'c', 1, 2, 3]

// concat 重写
Array.prototype.myConcat = function (items) {
  if (this == null) {
    throw new TypeError(this + 'is not defined')
  }
  var o = Object(this),
    newArr = [],
    length = arguments.length,
    index = 0
  if (Array.isArray(o)) {
    for (var i = 0; i < o.length; i++) {
      newArr[i] = o[i]
    }
  } else {
    newArr[0] = o
  }
  while (index < length) {
    var item = arguments[index]
    if (Array.isArray(item)) {
      // newArr = [].myConcat.apply(newArr, item) // 开启递归扁平化
      for (var i = 0; i < item.length; i++) {
        // 浅拷贝模式
        newArr[newArr.length] = item[i]
      }
    } else {
      newArr[newArr.length] = arguments[index]
    }
    index++
  }
  return newArr
}

Array.prototype.copyWithin

copyWithin 复制数组的一部分到数组中的另外一个位置,不会改变数组长度,不改变原数组

  • 第一个参数为 target 目标位置,从目标位置开始复制,默认值为 0; 必填
  • 第二个参数是被复制元素的起始位置(开始复制元素的起始位置) 默认值为 0 可选
  • 第三个参数是被复制元素结束位置(开始复制元素的结束位置) 默认值为 this.length 可选
  • 三个参数均可填负数 负数计算规则 length +( index ) 如超出 则为 length 如 小于 0 则为 0
  • copyWithin是一个可变方法,他不会改变this的长度length,但是会改变this本身的内容,且需要时会创建新的属性
// 使用方法
var arr = [1, 2, 3, 4, 5, 6]
console.log(arr.copyWithin(1)) // [1, 1, 2, 3, 4, 5]
console.log(arr.copyWithin(1, 2)) // [1, 3, 4, 5, 6, 6]
console.log(arr.copyWithin(1, 2, 4)) // [1, 3, 4, 4, 5, 6]
console.log(arr.copyWithin(1, -3, -1)) // [1, 4, 5, 4, 5, 6]
console.log(arr.copyWithin(-2, -3, -1)) // [1, 2, 3, 4, 4, 5]
// 重写copyWithin
Array.prototype.myCopyWithin = function (target) {
  if (this == null) {
    throw new TypeError(this + 'is not defined')
  }
  var o = Object(this),
    start = arguments[1] >> 0,
    end = arguments[2],
    len = o.length >> 0,
    res = []
  if (len < 0) {
    throw new TypeError('This is length not Number')
  }
  target = target >> 0
  end = end ? end >> 0 : len
  // 把参数转为正数 非法参数扶正
  target = target < 0 ? Math.max(target + len, 0) : Math.min(target, len)
  start = start < 0 ? Math.max(start + len, 0) : Math.min(start, len)
  end = end < 0 ? Math.max(end + len, 0) : Math.min(end, len)
  // 浅拷贝当前数组
  for (var i = 0; i < len; i++) {
    res[i] = o[i]
  }
  // 也可以直接解构
  // res = [...o]
  // 测算步数 如果 拷贝的长度 加上拷贝的位置大于数组长度则数组长度减去 起始位置 如果没有大于则正常步数
  // copyWithin 不会改变数组长度
  var step = end - start + target >= len ? len - target : end - start
  while (step) {
    o[target++] = res[start++]
    step--
  }
  return o
}

Array.prototype.entries

返回一个新的 Array iterator 对象,该对象中包含数组的 键/值

var arr = ['aa', 'bb']
var iterator = arr.entries()
console.log(iterator.next().value) >> [0,'aa']

Array.prototype.every

方法测试一个数组内所有的元素是否都能通过回调函数的测试,返回布尔值,如果测试的数组是一个空数组则返回true

  1. 第一个参数为 callBack,必填项
    1. callBack 中有三个参数,第一个参数为item (当前项) ,第二个参数为index为当前项的索引值,第三个参数则是数组整体
  2. 第二个参数为指定第一个参数的 this 指向,选填项
  3. 返回值: 如果回调函数中每一次返回的都是 truthy值,返回true,否则返回false
  4. every方法为数组中的每一个元素执行一次callBack函数,知道它找到一个会使callBackfalsy的元素,如果发现一个这样的元素,every方法则会立即返回false
  5. callBack会对已经赋值的索引调用,不会对那些已经被删除或者未被赋值的索引调用
  6. every若传入一个空数组.无论如何都会返回true
// 使用方法
var arr = [1, 2, 3, 4, 5]
var res = arr.every(function (item, index, array) {
  console.log(item, index, array)
  return item > 3
})
console.log(res) // false
// 重写 every
Array.prototype.myEvery = function (callBack) {
  if (this == null) {
    throw new TypeError(this + 'called on null or undefined')
  }
  var o = Object(this),
    len = o.length >> 0,
    arg2 = arguments[1],
    flag = true,
    step = 0,
    item
  if (len < 0) {
    throw new TypeError('Length is greater than 0')
  }
  while (step < len && flag) {
    item = o[step]
    flag = callBack.apply(arg2, [item, step, o])
    step++
  }
  return flag
}

Array.prototype.fill

fill 是使用固定值填充数组,从开始索引到结束索引,不包括结束索引,修改原数组,返回填充好的原数组

  1. 第一个参数 value为填充数组的值默认值为 undefined
    1. 如果 value 为引用类型,则填充的项都是同一个引用类型,例如 arr[1] === arr[0] >> true
  2. 第二个参数为 start 填充起始位置的索引默认值为 0
    1. 如果 start 为负数则为 start+length
  3. 第三个参数为end填充结束位置的索引默认值为 this.length
    1. 如果 end 为负数则为 end+length
// 使用方法
var arr = [1, 2, 3, 4, 5]
var res = arr.fill(4)
console.log(arr, res, arr === res) // [4, 4, 4, 4, 4] (5) [4, 4, 4, 4, 4] true
var arr = [1, 2, 3, 4, 5]
var res = arr.fill('aaa', 1)
console.log(arr, res, arr === res) // [1, "aaa", "aaa", "aaa", "aaa"] (5) [1, "aaa", "aaa", "aaa", "aaa"] true
var arr = [1, 2, 3, 4, 5]
var res = arr.fill('bbb', 1, 3)
console.log(arr, res, arr === res) // [1, "bbb", "bbb", 4, 5] (5) [1, "bbb", "bbb", 4, 5] true
var arr = [1, 2, 3, 4, 5]
var res = arr.fill('ccc', -3)
console.log(arr, res, arr === res) // [1, 2, "ccc", "ccc", "ccc"] (5) [1, 2, "ccc", "ccc", "ccc"] true
var arr = [1, 2, 3, 4, 5]
var res = arr.fill('ddd', -3, -2)
console.log(arr, res, arr === res) //  [1, 2, "ddd", 4, 5] (5) [1, 2, "ddd", 4, 5] true
var arr = [1, 2, 3, 4, 5]
var res = arr.fill('eee', null, null)
console.log(arr, res, arr === res) // [1, 2, 3, 4, 5] (5) [1, 2, 3, 4, 5] true
// fill 重写
Array.prototype.myFill = function (value) {
  if (this == null) {
    throw new TypeError(this + 'called on null or undefined')
  }
  var o = Object(this),
    len = o.length >> 0,
    step = 0,
    start = arguments[1] >> 0,
    end = arguments[2]
  if (len < 0) {
    throw new TypeError('length is greater than 0')
  }
  end = end === undefined ? len : end >> 0
  start = start >= 0 ? Math.min(len, start) : Math.max(start + len, 0)
  end = end >= 0 ? Math.min(len, end) : Math.max(end + len, 0)
  step = Math.max(end - start, 0)
  while (step) {
    o[start++] = value
    step--
  }
  return o
}

Array.prototype.filter

filter 据 MDN 描述,创建一个新数组,新数组中包含 filter 回调测试通过的所有元素;简单理解 filter 方法按照回调的返回的布尔值进行过滤,返回通过测试的元素组成的新数组,如过全部元素都不通过测试则返回空数组

  1. callBack 会对数组中的每个元素调用一次,并取 callBack 返回值为 true 的元素组成新数组,或者等价于 true 的返回值;
    1. 注: callBack 不会对已经删除的元素,或者未被赋值的元素进行调用
    2. 也就是说稀松数组则不会被遍历
  • 我们在重写的时候在思考 如何判断该元素未赋值,或者已经被删除
    • 第一个方法 使用 item === void 0 进行判断, void 0 === 元素为赋值,或者为 undefined, 显然这个办法不可用,为什么 因为数组中很可能有值为 undefined
    • 第二个方法 使用 Object.prototype.hasOwnProperty 来进行判断 ,这个方法是用来判断自身是否有该属性, 可行,符合当前应用场景;
    • 第三个方法 使用 in 语法来判断 判断该属性是否在指定对象或者指定对象的原型链中, 可行,但是与方法定义时的应用场景不符合;
// 使用方法
var arr = [1, undefined, , , 0, , 2, 3, 4, , 5, , , , 6]
var res = arr.filter((item) => item != null)
console.log(arr, res) // (15) [1, undefined, empty × 2, 0, empty, 2, 3, 4, empty, 5, empty × 3, 6] (7) [1, 0, 2, 3, 4, 5, 6]
// filter 重写
Array.prototype.myFilter = function (callBack) {
  if (this == null || this === '') {
    throw new TypeError(this + 'called on null or undefined')
  }
  var o = Object(this),
    len = o.length >> 0,
    res = [],
    toStr = Object.prototype.toString,
    has = Object.prototype.hasOwnProperty,
    arg1 = arguments[1],
    typeStr = '[object Function]',
    step = 0
  if (len < 0) {
    throw new TypeError('Length is greater than 0')
  }
  if (typeof callBack !== 'function' || toStr.call(callBack) !== typeStr) {
    throw new TypeError('callBack is not Function')
  }
  while (step < len) {
    if (!has.call(o, step)) {
      step++
      continue
    }
    if (callBack.apply(arg1, [o[step], step, o])) {
      // res.push(o[step])
      res[res.length] = o[step]
    }
    step++
  }
  return res
}

Array.prototype.find

find 返回数组中满足测试的第一个值,如果都不满足则返回undefined,简单理解,find 按照回调的返回值查找数组中第一个满足回调的项并返回,如果全部不满足返回undefined

  • 备注: 如果需要查询该数组中第一个满足条件得索引请使用findIndex
  • 备注: 如果需要查询该数组中是否存在该元素,请使用Array.prototype.indexOfArray.prototype.includes
  • find 有两个参数
    1. 第一个参数为 callBack
      1. callBack 中有三个参数,参数参考 数组迭代方法中的 map filter forEach some every findIndex
    2. 第二个参数为 this 指向
  • 注意: 该方法的 callBack 会对数组中的每一项进行调用,也就是说从数组索引0到数组索引length-1 ,这也就是说稀松数组,或者已被删除的项也会进行遍历,该方法的效率会低于那些只遍历有值的索引方法
  • 注意: 该方法第一次调用callBack时会确定元素索引值范围,例如:find方法执行之后在向数组中添加新元素则不会被callBack访问到,也就是说他不再本次遍历的范围内;如果在遍历执行时,改变为被 callBack 访问的元素,则访问的是当前的值,例如:callBack在访问数组索引为0的元素时,改变了数组为索引为1的元素,当callBack访问数组索引为1的元素时该元素已经被改变了则访问的是被改变的当前元素
// 当前回调中改变了数组中的项
var arr = [1, 2, 3, 4, 5, 6, 7]
var res = arr.find((item, index, array) => {
  if (index === 0) {
    array[1] = 999
  }
  return item > 100
})
console.log(res, arr) // 999 (7) [1, 999, 3, 4, 5, 6, 7]
// 使用方法
var json = [
  {
    name: 'zhangsan',
    id: 1,
  },
  {
    name: 'lisi',
    id: 2,
  },
  {
    name: 'wangwu',
    id: 3,
  },
  {
    name: 'zhaoliu',
    id: 4,
  },
]
var item = json.find(function (item, index, array) {
  return item.name === 'lisi'
})
console.log(item, json) // {name: "lisi", id: 2}  [{…}, {…}, {…}, {…}]
// find 重写
Array.prototype.myFind = function (callBack) {
  if (this == null || this === '') {
    throw new TypeError(this + 'called on null or undefined')
  }
  var o = Object(this),
    len = o.length >> 0,
    toStr = Object.prototype.toString,
    typeStr = '[object Function]',
    step = 0,
    arg1 = arguments[1]
  if (len < 0) {
    throw new TypeError('Length is greater than 0')
  }
  if (typeof callBack !== 'function' || toStr.call(callBack) !== typeStr) {
    throw new TypeError('callBack is not Function')
  }
  while (step < len) {
    if (callBack.apply(arg1, [o[step], step, o])) {
      return o[step]
    }
    step++
  }
  // return undefined
}

Array.prototype.findIndex

findexIndex 返回数组种满足测试的第一项索引,如果没有找到则返回-1

  • 参数与 find 一致
  • callBack调用规则与find 一致,会遍历稀松数组
// 使用方法
var arr = [22, 333, 444, 55, 66, 77]
var res = arr.findIndex((item) => item > 333)
console.log(arr, res) //  [22, 333, 444, 55, 66, 77] 2
// findIndex 重写
Array.prototype.myFindIndex = function (callBack) {
  if (this == null) {
    throw new TypeError(this + 'called on null or undefined')
  }
  var o = Object(this),
    len = o.length >> 0,
    arg1 = arguments[1],
    step = 0,
    toStr = Object.prototype.toString,
    typeStr = '[object Function]'
  if (typeof callBack !== 'function' || toStr.call(callBack) !== typeStr) {
    throw new TypeError('CallBack is not Function')
  }
  if (len < 0) {
    throw new TypeError('Length is greater than 0')
  }
  while (step < len) {
    if (callBack.apply(arg1, [o[step], step, o])) {
      return step
    }
    step++
  }
  return -1
}

Array.prototype.flat

flat 方法会按照一个指定的递归深度遍历数组,并把数组中的元素遍历到新数组返回,注意 flat 会 移除数组中的空项

  • flat 有一个参数 默认值为 1, 参数为递归的深度

    • 注意: 如果想要扁平化任意深度的数组,参数可以传入Infinity
    // 使用方法
    var arr = [1, [2, [3, [4]]]]
    var res = arr.flat()
    console.log(res) // [1, 2, Array(2)]
    var res = arr.flat(Infinity)
    console.log(res) // [1, 2, 3, 4]
    // flat 重写
    // 第一种方法 使用 reduce + concat 进行替代
    Array.prototype.myFlat = function (depth = 1) {
      var o = Object(this)
      return depth > 0
        ? o.reduce(function (prev, item) {
            return prev.myConcat(Array.isArray(item) ? item.myFlat(depth - 1) : item)
          }, [])
        : o.slice()
    }
    // 第二种方法 使用forEach  + 闭包 + push 进行替代
    Array.prototype.myFlat = function (depth = 1) {
      var o = Object(this),
        result = []
      ;(function flat(value, depth) {
        value.forEach(function (item) {
          if (Array.isArray(item) && depth > 0) {
            flat(item, depth - 1)
          } else {
            result.push(item)
          }
        })
      })(o, depth)
      return result
    }
    // 第三种方法 使用for循环 + 闭包 + void 0 + push 进行替代
    Array.prototype.myFlat = function (depth = 1) {
      var o = Object(this),
        result = []
      ;(function flat(value, depth) {
        var len = o.length,
          item
        for (var i = 0; i < len; i++) {
          item = value[i]
          if (item !== void 0) {
            if (Array.isArray(item) && depth > 0) {
              flat(item, depth - 1)
            } else {
              result.push(item)
            }
          }
        }
      })(o, depth)
      return result
    }
    // 第四种方法 使用 while循环 + push + pop + reverse 进行替代 比较重点 堆栈方法 缺点 深度难控制
    Array.prototype.myFlatten = function () {
      var o = Object(this),
        stack = [...o],
        item,
        result = []
      while (stack.length) {
        item = stack.pop()
        if (Array.isArray(item)) {
          stack.push(...item)
        } else {
          item !== void 0 && result.push(item)
        }
      }
      return result.reverse()
    }
    // 第五种方法 使用 generator 展开运算符进行替代
    function* flatten(array) {
      for (var item of array) {
        if (Array.isArray(item)) {
          yield* flatten(item)
        } else {
          if (item !== void 0) {
            yield item
          }
        }
      }
    }
    

Array.prototype.flatMap

flatMap 映射到数组的每一个元素,然后结果压缩成一个新数组,也就是相当于 一个 depth 为 1 的 flat 加上一个 map,但是比 map 效率快

  • flatMap 有两个参数
    1. 第一个参数为callBack回调函数,与 map 的参数一致,使用方法一致
      1. callBack 不会对已经删除的元素或者稀松数组进行调用
    2. 第二个参数为 this 指向用来指定 callBack 的回调
// 使用方法
var arr = [1, 2, 3, 4, 5, 6]
var res = arr.flatMap(function (item, index) {
  return [`${item} * ${index}`, item * index]
})
console.log(arr, res) // ) [1, 2, 3, 4, 5, 6]  ["1 * 0", 0, "2 * 1", 2, "3 * 2", 6, "4 * 3", 12, "5 * 4", 20, "6 * 5", 30]
// flatMap 重写
Array.prototype.myFlatMap = function (callBack) {
  if (this == null) {
    throw new TypeError(this + 'called on null or undefined')
  }
  var o = Object(this),
    len = o.length >> 0,
    arg1 = arguments[1],
    toStr = Object.prototype.toString,
    typeStr = '[object Function]',
    has = Object.prototype.hasOwnProperty,
    step = 0,
    item,
    result = []

  if (len < 0) {
    throw new TypeError('Length greater than 0')
  }

  if (typeof callBack !== 'function' || toStr.call(callBack) !== typeStr) {
    throw new TypeError('callBack is not Funtion')
  }
  while (step < len) {
    item = o[step]

    if (has.call(o, step)) {
      result = result.concat(callBack.apply(arg1, [item, step, o]))
    }
    step++
  }
  return result
}

Array.prototype.forEach

forEach 方法会对每一个元素执行一次 CallBack 方法,返回值为undefined

  • forEach 方法会对每一项执行一次CallBack函数,会忽略已经被删除的元素,或者未被初始化的元素,例如(稀松数组)

  • forEach遍历的范围在第一次调用callBack前就已经确定,调用 forEach 后添加到数组中的项将不会被callBack访问,如果已经存在的值被改变,则传递给callBack的值是遍历到他们那一刻的值,如果已经访问的元素在迭代时被删除了,之后的元素将会被跳过,例如迭代时使用 shift 将元素删除

  • forEach的第二个参数,this,如过 this 有值,则 callBack 被调用时,callBack 会指向这个 this,如果 this 没有值,callBack 调用时会指向全局的 this

  • forEach被调用时不会改变他的对象,但是他的对象可能会被callBack所改变

  • 除抛异常外无法终止forEach

    // 使用方法
    // 1. forEach 不会对未初始化的值进行任何操作(稀松数组),如下
    var arr = [1, , , , 2, , , , 3, , , 4]
    arr.forEach(function (item, index) {
      console.log(item, index)
    })
    // 2. 扁平化数组
    var arr = [1, 2, 3, [4, 5, [6, 7], 8, 9]]
    
    function _flatten(arr) {
      var result = []
      console.log(arr)
      arr.forEach((item) => {
        if (Array.isArray(item)) {
          result.push(..._flatten(item))
        } else {
          result.push(item)
        }
      })
      return result
    }
    
    console.log(_flatten(arr)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
    // forEach 重写
    Array.prototype.myForEach = function (callBack) {
      if (this == null) {
        throw new TypeError(this + 'called on null or undefined')
      }
      var o = Object(this),
        arg1 = arguments[1],
        len = o.length >> 0,
        index = 0,
        toStr = Object.prototype.toString,
        typeStr = '[object Function]',
        has = Object.prototype.hasOwnProperty,
        item
      if (len < 0) {
        throw new TypeError('The length must be greater than 0')
      }
      if (typeof callBack !== 'function' || toStr.call(callBack) !== typeStr) {
        throw new TypeError('callBack is not Funtion')
      }
      while (index < len) {
        if (has.call(o, index)) {
          item = o[index]
          callBack.apply(arg1, [item, index, o])
        }
        index++
      }
    }
    

Array.form

Array.from 方法从一个类数组或可迭代对象创建一个新的浅拷贝的数组

  • Array.from有三个参数
    • 第一个参数是(arrayLike)想要转成数组的为数组对象,或可迭代对象(必选)
    • 第二个参数是(mapFn)如果制定了该参数,新数组中的每个元素都会执行此回调函数,并将结果一一映射新数组(可选)
      • 也就是说Array.from(obj,mapFn,thisArg)相当于Array.from(obj).map
    • 第三个参数(thisArg)可选参数,执行回调函数mapFn时的this指向(可选)
  • Array.from返回一个新的数组实例
// 使用方法
// 1. String生成数组
Array.from('foo');
// [ "f", "o", "o" ]
// 2. Set生成数组
const set = new Set(['foo', 'bar', 'baz', 'foo']);
Array.from(set);
// [ "foo", "bar", "baz" ]
// 3. Map生成数组
const map = new Map([[1, 2], [2, 4], [4, 8]]);
Array.from(map);
// [[1, 2], [2, 4], [4, 8]]

const mapper = new Map([['1', 'a'], ['2', 'b']]);
Array.from(mapper.values());
// ['a', 'b'];

Array.from(mapper.keys());
// ['1', '2'];

// 常用方法
// 1. 数组合并去重
function combine(){
    let arr = [].concat.apply([], arguments);  //没有去重复的新数组
    return Array.from(new Set(arr));
}

var m = [1, 2, 2], n = [2,3,3];
console.log(combine(m,n));                     // [1, 2, 3]

// Array.from 重写
Array.myFrom = (function () {
  var toStr = Object.prototype.toString;
  // 工具函数 判断是不是函数
  var isCallable = function (fn) {
    return typeof fn === 'function' || toStr.call(fn) === '[object Function]'
  }
  // 工具函数 抛错
  var throwError = function (msg) {
    throw new TypeError(msg)
  }
  // 工具函数 判断该类型是否为可迭代对象
  var isIterator = function (obj) {
    return obj && obj[Symbol.iterator] ? isCallable(obj[Symbol.iterator]) : false
  }
  // 工具变量 数组安全
  var maxSafeInteger = Math.pow(2, 53) - 1;
  // 工具函数 浅拷贝 将可迭代对象 拷贝为数组
  var cloneIteratorToArray = function (it) {
    var result = []
    for (var item of it) {
      result.push(item)
    }
    return result;
  }

  return function from(items) {
    // 如果this为空或者undefined
    if (this == null) throwError(this + ' called on null or undefined')
    // 如果参数不是迭代对象抛错
    // if (!isIterator(items)) throwError(items + ' is not Iterator')
    var o = Object(items),
      result = [],
      callBack = arguments[1],
      thisArg = arguments[2];
    if (!isIterator(items)) {
      return result
    }
    // 如果有回调函数
    if (callBack) {
      // callBack不是函数抛错
      if (!isCallable(callBack)) throwError('callBack is not Function')
      if (isIterator(o)) {
        // 临时储存数组
        var tempArray = cloneIteratorToArray(o);
        var len = Math.max(0, Math.min(tempArray.length, maxSafeInteger)),
          item,
          index = 0;
        while (index < len) {
          item = tempArray[index];
          result[index] = callBack.apply(thisArg, [item, index])
          index++;
        }
      }
    } else {
      result = cloneIteratorToArray(o)
    }
    return result;
  }
})();

Array.prototype.includes

includes 判断一个数组中是否包含一个指定的值,如果包含返回true,否则返回false

  • includes 有两个参数
    • 第一个参数为(valueToFind)需要查找元素的值,,如果不填则默认为undefined(必选)
      • 注意:使用includes比较字符串时区分大小写
    • 第二个参数为(fromIndex)从formIndex索引处开始查找valueToFind,如果为负值则按照array.length + fromIndex的索引开始搜,默认值为0
      • 如果fromIndex大于数组的长度则会直接返回false,数组则不会被搜索
      • 如果fromIndex小于为负值,计算出的索引将作为searchElement的位置,如果计算出的索引小于0则整个数组都会被搜索
  • 返回值
    • 返回一个布尔值,如果在数组中找到了则返回true(如果填写了fromIndex,则在fromIndex指定的索引的范围查找,找到了返回true)
      • 0值是默认相等的不管符号是什么,但是false不会被认为0
  • includes方法被设计为通用方法,他不要求this的值为数组对象,所以可以被用于其他类型的对象,如Arguments
// 使用方法
var arr = [1, 2, 3, 4, 5, 6]
var res = arr.includes(3)
console.log(res); // true

var arr = [1, 2, 3, 4, 5, 6]
var res = arr.includes(3, 3)
console.log(res); // false

var arr = [1, 2, 3, 4, 5, 6]
var res = arr.includes(3, -3)
console.log(res); // false

var arr = [1, 2, 3, 4, 5, 6]
var res = arr.includes(3, -33) // arr.length + (-33) < 0 则等于0
console.log(res); // false

(function () {
  console.log([].includes.call(arguments, 'a'));
  console.log([].includes.call(arguments, 1));
})('a', 'b', 1, 2, 3);

// includes 重写
Array.prototype.myIncludes = function (valueToFind) {
  if (this == null) {
    throw new TypeError(this + ' called on null or undefined')
  }
  var o = Object(this),
    fromIndex = arguments[1] >> 0,
    len = o.length >> 0;
  if (len < 0) {
    throw new TypeError('The length must be greater than 0 ')
  }
  // 如果数组的长度小于传入的指定索引 则不查找直接返回false
  if (fromIndex >= len) {
    return false
  }
  fromIndex = fromIndex < 0 ? Math.max(0, len + fromIndex) : Math.min(len, fromIndex)
  // 零值相等
  var sameValueZero = function (x, y) {
    return x === y || x !== x && y !== y
  }
  while (fromIndex < len) {
    if (sameValueZero(valueToFind, o[fromIndex])) {
      return true
    }
    fromIndex++;
  }
  return false;
};

Array.prototype.indexOf

indexOf 方法可以返回数组中可以找到的第一个元素的索引,如不存在则返回 -1

  • indexof 有两个参数
    • 第一个参数 searchElement 要查找的元素
    • 第二个参数 fromIndex 开始查找的位置,如果fromIndex参数为负数,则取length + fromIndex如果取完后小于0那么则是0,否则为length + fromIndex,如果fromIndex为正数且大于当前要查找的数组长度,则直接为-1
  • 返回值 首个被找到的元素的索引,如果没有找到则为-1
  • indexOf使用strtic equality(严格相等)进行判断searchElement与数组中包含的元素之间的关系
// indexOf 使用方法
// 1. 查找元素的位置
var arr = [NaN, 2, 3, 4, 5, undefined];
arr.indexOf(undefined) // 5
arr.indexOf(NaN) // -1
arr.indexOf(2) // 1
// 2. 查找所有指定元素在数组中的位置
var arr = [111, 222, 111, 333, 111, 222, 111];
var searchElementIndex = function (array, searchElement) {
  var indices = [];
  // (function from(array, searchElement, idx) {
  //   if (idx === undefined) {
  //     idx = array.indexOf(searchElement);
  //   } else {
  //     idx = array.indexOf(searchElement, idx);
  //   }
  //   if (idx !== -1) {
  //     indices.push(idx)
  //     from(array, searchElement, idx + 1)
  //   }
  // })(array, searchElement);
  var idx = array.indexOf(searchElement)
  while (idx !== -1) {
    indices.push(idx);
    idx = array.indexOf(searchElement, idx + 1)
  }
  return indices
}
console.log(searchElementIndex(arr, 111)); // [ 0, 2, 4, 6 ]

// indexOf 重写
Array.prototype.myIndexOf = function (searchElement) {
  if (this == null) {
    throw new TypeError(this + ' called on null or undefined')
  }
  var o = Object(this),
    len = o.length >> 0,
    fromIndex = arguments[1] >> 0,
    item,
    step;
  if (len < 0) {
    throw new TypeError('The length must be greater than 0')
  }
  step = fromIndex >= 0 ? Math.min(len, fromIndex) : Math.max(0, fromIndex + len);
  /// 判断同值相等
  var sameValue = function (x, y) {
    return x === y
  }
  // 如果传入查找元素的索引位置大于当前或者等于当前的数组位置则直接返回-1
  if (step >= len) return -1

  while (step < len) {
    item = o[step];
    if (sameValue(searchElement, item)) {
      return step;
    }
    step++;
  }
  return -1
}

Array.isArray

Array.isArray 用于确定传递的值是否是一个Array

  • Array.isArray 有一个参数
    • 参数为需要检测的值obj
  • 返回值 如果值是Array则返回true(不包括TypedArray),否则返回false
  • 当检测Array实例时,Array.isArray优于instanceof,因为Array.isArray能检测iframes
// Array.isArray 使用方法
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray(function () {})); // false
console.log(Array.isArray(Array.prototype)); // true 
// 我们在使用Array.isArray进行检测的时候发现,Array.prototype也是一个数组
// Array.isArray 重写
Array.myIsArray = function (obj) {
  var toStr = Object.prototype.toString,
    typeStr = '[object Array]';
  return toStr.call(obj) === typeStr
}

Array.prototype.join

join 将一个数组,或者类数组的所有元素连接成一个字符串,并返回字符串,如果数组中只有一个项目,那么直接返回这个数组不使用分隔符

  • join有一个参数
    • separator(分隔符)指定一个字符串来分割数组的每个元素.如果需要,将分割符转换为字符串.如果缺少该值,数组元素则用,分割.如果separator是空字符串("")则所有元素置间没有分割符 (可选参数) 默认值为,
  • 返回值为一个数组元素连接的字符串,如果arr.length为0,则返回空字符串
  • 注意: 如果元素为undefinednull,它会呗转换为空字符串
// 使用方法
var a = ['Wind', 'Rain', 'Fire'];
var result = a.join() // Wind,Rain,Fire
var result = a.join('<') // Wind<Rain<Fire
var result = a.join('+') // Wind+Rain+Fire
var result = a.join(null)// WindnullRainnullFire
var result = a.join('')// WindRainFire
// join 重写
Array.prototype.myJoin = function (separator) {
  if (this == null) {
    throw new TypeError(this + ' called on null or undefined')
  }
  var o = Object(this),
    len = o.length >> 0,
    item,
    str = '';
  separator = separator === undefined ? ',' : String(separator);
  if (len < 0) {
    throw new TypeError('The length must greater than 0')
  }
  if (len === 0) {
    return str;
  }
  for (var i = 0; i < len; i++) {
    item = o[i] == null ? '' : o[i];
    // str += item + (i === len - 1 ? '' : separator)
    str += item + separator
  }
  return str.slice(0, -separator.length);
}

Array.prototype.keys

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

  • 没有参数
  • 返回值是一个新的Array迭代器对象,迭代器对象中是每一个索引,包获已经删除的或者为赋值的索引
// 使用方法
var arr = [, , , , 1, 2, 3]
var sparseKeys = Object.keys(arr) //  ["4", "5", "6"]
var denseKeys = [...arr.keys()] //  [0, 1, 2, 3, 4, 5, 6]
// keys 重写
Array.prototype.myKeys = function* () {
  if (this == null) {
    throw new TypeError(this + ' called on null or undefined')
  }
  var o = Object(this),
    len = o.length >> 0;
  if (len < 0) {
    throw new TypeError('The length must be greater than 0')
  }
  for (var i = 0; i < len; i++) {
    yield(i)
  }
}

Array.prototype.lastIndexOf