跳至主要內容

Array

Mr.LRH大约 53 分钟

Array

Array 概述

数组是一种类列表对象,是值的有序集合,它的键名是按次序排列的一组整数(0,1,2...)。

  • JavaScript 数组使用一个 32 位整数保存数组的元素个数。数组的索引从 0 开始,数组最大能容纳 4294967295(即 2^32-1)个元素。

    数组元素是对象属性,JavaScript 语法要求使用方括号表示法(或用引号包裹数组下标)访问以数字开头的属性。

    const arr = ['a', 'b', 'c', 'd', 'e', 'f']
    
    // JavaScript 引擎通过隐式的 toString,将 arr[1] 中的 1 强制转换为字符串。
    arr[1] // b
    arr[1.00] // b
    arr['1'] // b
    
    // '1' 和 '01' 将指向 arr 对象上的两个不同的槽位
    arr['01'] // undefined
    
    // 使用 Object.keys 方法返回数组的所有键名
    const arr = ['a', 'b', 'c', 'd', 'e', 'f']
    Object.keys(arr) // ['0', '1', '2', '3', '4', '5']
    
  • 每个 JavaScript 数组都有一个 length 属性,该属性是一个动态值。针对非稀疏数组,length 属性就是数组元素的个数;针对稀疏数组,length 比所有元素的索引都要大。

    let arr = ['a', 'b', 'c'] // arr.length 为 3
    console.log(arr.length) // 3
    
    // 设置数组的 length 属性【小于】当前数组成员个数的值
    // 该数组的成员数量会自动【减少】到 length 设置的值
    arr.length = 2
    console.log(arr) // ['a', 'b']
    
    // 设置数组的 length 属性【大于】当前数组成员个数的值
    // 该数组的成员数量会自动【增加】到 length 设置的值,新增的位置都是空位
    // 读取新增的位置会返回 undefined
    arr.length = 4
    console.log(arr) // ['a', 'b', empty × 2]
    console.log(arr[3]) // undefined
    
    // 清空数组的一个有效方法,就是设置数组的 length 属性为 0
    arr.length = 0
    console.log(arr) // []
    
    // 设置数组的 length 属性为不合法的值,JavaScript 会报错
    [].length = -1 // Uncaught RangeError: Invalid array length
    [].length = Math.pow(2, 32) // Uncaught RangeError: Invalid array length
    [].length = 'abc' // Uncaught RangeError: Invalid array length
    
    // 数组本质上是一种对象,可以为数组添加属性,但是这影响 length 属性的值
    // length 属性的值等于最大的数字键加 1,数组如果没有整数键,所以 length 属性保持为 0
    let arr = []
    arr['abc'] = 'abc'
    console.log(arr.length) // 0
    arr[2.1] = 'abc'
    console.log(arr.length) // 0
    console.log(arr) // [abc: 'abc', 2.1: 'abc']
    
    // 数组的键名添加超过范围的数值,该键名会自动转为字符串
    let arr = []
    arr[-1] = 'a'
    arr[Math.pow(2, 32)] = 'b'
    console.log(arr.length) // 0
    console.log(arr) // [-1: 'a', 4294967296: 'b']
    console.log(arr[-1]) // 'a'
    console.log(arr[4294967296]) // 'b'
    
  • JavaScript 数组是动态的,并且可以包含不同的数据类型。

    • 在创建数组时无需声明一个固定的大小或者在数组大小变化时无需重新分配空间。
    • 数组中包含数组称之为多维数组。可以通过将两组方括号链接在一起来访问数组内的另一个数组。
  • JavaScript 数组不是关联数组(具有命名索引的数组,可以使用字符串或者其他类型的值作为索引),不能使用任意字符串作为索引访问数组元素,必须使用非负整数(或者非负整数的字符串形式)作为索引访问。

  • JavaScript 数组可能是稀疏数组,数组元素的索引不一定要连续的,它们之间可以有空缺。

    • 稀疏数组的特性

      • 含有空元素 empty 的数组。索引不连续,数组长度大于元素个数的数组。数组的空位是可读取的,返回 undefined
      • 在大多数遍历数组的方法中,遇到 empty 元素的时候,callback 函数是不会执行的。比如 forEachfor...in 以及 Object.keys 方法进行遍历,空位都会被跳过。如果某个位置是 undefined,遍历的时候则不会被跳过。
      • 稀疏数组在访问元素的速度上比密集数组慢。
      • 稀疏数组在一些数组方法中与密集数组存在差异。
    • 生成稀疏数组

      let sparseArr01 = new Array(5)
      console.log('sparseArr01 : ', sparseArr01)
      // sparseArr02 : [empty × 5]
      
      let sparseArr02 = []
      sparseArr02[4] = 4
      console.log('sparseArr02 : ', sparseArr02)
      // sparseArr02 : [empty × 4, 5]
      
      let sparseArr03 = []
      sparseArr03.length = 5
      console.log('sparseArr03 : ', sparseArr03)
      // sparseArr03 : [empty × 5]
      
      let sparseArr04 = [0, , , , ,]
      console.log('sparseArr04 : ', sparseArr04)
      // sparseArr04 : [0, empty × 4]
      
      let sparseArr05 = [0, 1, 2, 3, 4]
      delete sparseArr05[4]
      console.log('sparseArr05 : ', sparseArr05)
      // sparseArr05 : [0, 1, 2, 3, empty]
      
    • empty VS undefined

      let sparseArr = new Array(3)
      console.log(sparseArr) // [empty × 3]
      console.log(sparseArr[0]) // undefined
      
      let undefinedArr = [undefined, undefined, undefined]
      console.log(undefinedArr) // [undefined, undefined, undefined]
      console.log(undefinedArr[0]) // undefined
      
      sparseArr.forEach(item => console.log(item))
      // 无 console.log 输出
      undefinedArr.forEach(item => console.log(item))
      // 输出如下:
      // undefined
      // undefined
      // undefined
      
      // 使用 in 运算符判断指定属性是否在指定对象或原型链中
      // sparseArr 的键名都是空的
      // undefinedArr 的键名是有值的
      0 in sparseArr // false
      0 in undefinedArr // true
      
    • 稀疏数组转密集数组

      let sparseArr = [0, , , , ,]
      console.log('sparseArr : ', sparseArr) // sparseArr : [0, empty × 4]
      
      Array.apply(null, sparseArr) // [0, undefined, undefined, undefined, undefined]
      Array.from(sparseArr) // [0, undefined, undefined, undefined, undefined]
      [...sparseArr] // // [0, undefined, undefined, undefined, undefined]
      

创建数组

  • 数组直接量(字面量)

    let emptyArr = [] // 没有元素的数组
    
    let number = 1
    let numberArr = [number, number + 1, number + 2]
    
  • Array() 构造函数:用于创建 Array 对象。

    // 无参数时,返回一个空数组
    new Array() // []
    
    // 单个正整数参数,表示返回的新数组的长度
    new Array(1) // [empty]
    new Array(2) // [empty × 2]
    
    // 单个非数值(比如字符串、布尔值、对象等)作为参数,
    // 则该参数是返回的新数组的成员
    new Array('abc') // ['abc']
    new Array([1]) // [Array(1)]
    
    // 多参数时,所有参数都是返回的新数组的成员
    new Array(1, 2) // [1, 2]
    new Array('a', 'b', 'c') // ['a', 'b', 'c']
    
    // 非正整数的数值作为参数,会报错
    new Array(3.2) // Uncaught RangeError: Invalid array length
    new Array(-3) // Uncaught RangeError: Invalid array length
    
  • Array.of()

    语法: Array.of(element0, element1, /* … ,*/ elementN)

    描述: 通过参数值以及参数顺序创建一个新的 Array 实例,而不考虑参数的数量或类型。

    Array.of() // []
    Array.of(undefined) // [undefined]
    Array.of(1) // [1]
    Array.of(1, 2) // [1, 2]
    Array.of(1, { a: 1 }, null, undefined) // [1, { a: 1 }, null, undefined]
    
    // 模拟 Array.of()
    function ArrayOf() {
      return [].slice.call(arguments)
    }
    
  • Array.from()

    语法: Array.from(arrayLike, mapFn, thisArg)

    描述: 通过类数组对象或者可迭代对象(包括 SetMap 数据结构)创建一个新的浅拷贝的数组实例。

    参数:

    • arrayLike :需要转换成数组的类数组对象或可迭代对象。
      • 类数组对象:带有 length 属性和索引元素的对象。比如: document.getElementsByTagName() 返回的 NodeListarguments 等对象。
      • 可迭代对象:如 StringSetMap
    • mapFn :可选值。arrayLike 中的值都会执行 mapFn 回调函数,函数返回值将添加到数组中。该函数接收以下参数:
      • element :数组当前正在处理的元素
      • index :数组当前正在处理元素的索引
    • thisArg :可选值。执行 mapFn 时用作 this 的值。
    // 如果参数是真正的数组,Array.from() 会返回相同的数组
    Array.from([1, 2, 3]) // [1, 2, 3]
    
    Array.from([1, , 3], element => element || 0) // [1, 0, 3]
    
    // 类数组对象:其本质特征必须有 length 属性
    // 比如: document.getElementsByTagName() 返回的 NodeList 、 arguments 等对象
    
    let arrayLike = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3,
    }
    
    // ES5 的写法
    let arr1 = [].slice.call(arrayLike) // ['a', 'b', 'c']
    
    Array.from(arrayLike) // ['a', 'b', 'c'] ,ES6 的写法
    
    Array.from(arrayLike, (element, index) => element + index) // ['a0', 'b1', 'c2']
    // 等同于 Array.from(arrayLike).map((element, index) => element + index)
    
    Array.from({ length: 3 }) // [undefined, undefined, undefined]
    Array.from({ length: 3 }, (element, index) => index) // [0, 1, 2]
    
    // 根据 DOM 元素的属性创建一个数组
    const images = document.querySelectorAll('img')
    const sources = Array.from(images, (image) => image.src)
    
    // 部署了 Iterator 接口的数据结构,Array.from() 都能将其转为数组。
    // 比如:String、Set、Map
    
    Array.from('hello') // ['h', 'e', 'l', 'l', 'o']
    
    Array.from(new Set(['a', 'b'])) // ['a', 'b']
    
    let map01 = new Map([
      ['1', 'a'],
      ['2', 'b'],
    ])
    Array.from(map01) // [['1', 'a'], ['2', 'b']] 
    Array.from(map01.values()) // ['a', 'b']
    Array.from(map01.keys()) // ['1', '2']
    

    Array.from() 方法的应用:

    • 进行数组合并去重

      function combine() {
        let arr = [].concat.apply([], arguments)
        // 没有去重复的新数组,之后用 Set 数据结构的特性来去重
        return Array.from(new Set(arr))
      }
      
      const arr01 = [1, 2, 2]
      const arr02 = [2, 3, 3]
      console.log(combine(arr01, arr02)) // [1, 2, 3]
      
    • 将字符串转换为数组,返回字符串长度,避免 JavaScript 将大于 \uFFFF 的 Unicode 字符算作两个字符的问题。

      function countSymbols(string) {
        return Array.from(string).length
      }
      
  • Array.fromAsync()

    语法: Array.fromAsync(arrayLike, mapFn, thisArg)

    描述: 通过异步可迭代对象、可迭代对象或类数组对象创建一个新的浅拷贝Array 实例。

    参数:

    • arrayLike :需要转换为数组的异步可迭代、可迭代或类数组对象。
      • 异步可迭代对象: ReadableStreamAsyncGenerator
      • 可迭代对象: StringSetMap
      • 类数组对象:带有 length 属性和索引元素的对象。比如: document.getElementsByTagName() 返回的 NodeListarguments 等对象。
    • mapFn :可选值。arrayLike 中的值都会执行 mapFn 回调函数,函数返回值将添加到数组中(在等待兑现后)。该函数接收以下参数:
      • element :数组当前正在处理的元素。由于所有元素都会先等待其兑现,因此该值永远不会是 thenable
      • index :数组当前正在处理元素的索引
    • thisArg :可选值。执行 mapFn 时用作 this 的值。

    Array.fromAsync() 在行为上与 Array.from() 几乎等价,主要区别如下:

    • Array.fromAsync() 可以处理异步可迭代对象。
    • Array.fromAsync() 返回一个会兑现为数组实例的 Promise
    • 如果使用非异步可迭代对象调用 Array.fromAsync(),则要添加到数组中的每个元素(无论是否为 Promise)都会先等待其兑现。
    • 如果提供了 mapFn,则其输入和输出会在内部等待兑现。

    Array.fromAsync()Promise.all() 都可以将一个 Promise 可迭代对象转换为一个数组的 Promise。主要区别如下:

    • Array.fromAsync() 会依次等待对象中产生的每个值兑现。Promise.all() 会并行等待所有值兑现。
    • Array.fromAsync() 惰性迭代可迭代对象,并且不会获取下一个值,直到当前值被兑现。Promise.all() 预先获取所有值并等待它们全部兑现。
    const asyncIterable = (async function* () {
      for (let i = 0; i < 5; i++) {
        await new Promise((resolve) => setTimeout(resolve, 10 * i))
        yield i
      }
    })()
    Array.fromAsync(asyncIterable).then((array) => console.log(array))
    // [0, 1, 2, 3, 4]
    
    Array.fromAsync(
      new Map([
        [1, 2],
        [3, 4],
      ])
    ).then((array) => console.log(array))
    // [[1, 2], [3, 4]]
    
    Array.fromAsync(
      new Set([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)])
    ).then((array) => console.log(array))
    // [1, 2, 3]
    
    Array.fromAsync({
      length: 3,
      0: Promise.resolve(1),
      1: Promise.resolve(2),
      2: Promise.resolve(3),
    }).then((array) => console.log(array))
    // [1, 2, 3]
    

检测数组

  • Array.isArray()

    语法: Array.isArray(value)

    描述: 用于确认 value 是否是一个 Array

    参数:

    • 如果 valueArray 实例,则返回为 true,否则为 false
    • 如果 valueTypeArray 实例(如:Int8ArrayUint8Array 等),则返回为 false
    // 以下的函数调用都返回 true
    Array.isArray([])
    Array.isArray([1])
    Array.isArray(new Array())
    Array.isArray(new Array('a', 'b', 'c', 'd'))
    Array.isArray(new Array(3))
    Array.isArray(Array.prototype) // Array.prototype 也是一个数组
    
    // 以下的函数调用都返回 false
    Array.isArray()
    Array.isArray({})
    Array.isArray(null)
    Array.isArray(undefined)
    Array.isArray(17)
    Array.isArray('Array')
    Array.isArray(true)
    Array.isArray(false)
    Array.isArray(new Uint8Array(32))
    // 这不是一个数组,因为不是使用数组字面量语法或 Array 构造函数创建的
    Array.isArray({ __proto__: Array.prototype })
    

    instanceof VS Array.isArray()

    • Array.isArray() 优于 instanceof,因为 Array.isArray() 能跨领域工作。
    • 如果网页里有多个框架,则可能涉及两个不同的全局执行上下文,可能存在有两个不同版本的 Array 构造函数。如果要把数组从一个框架传给另一个框架,则这个数组的构造函数将有别于在第二个框架内本地创建的数组。使用 Array.isArray() 可以不用区分 Array 是在哪个全局执行上下文中创建的。
    const iframe = document.createElement('iframe')
    document.body.appendChild(iframe)
    const xArray = window.frames[window.frames.length - 1].Array
    const arr = new xArray(1, 2, 3) // [1, 2, 3]
    
    // 正确检查 Array
    Array.isArray(arr) // true
    // arr 的原型是 xArray.prototype,不同于 Array.prototype 的对象
    arr instanceof Array // false
    

迭代器方法

  • Array.prototype.keys()

    语法: arr.keys()

    描述: 返回一个新的数组迭代器对象,其中包含数组中每个索引的键。

    • 当读取稀疏数组的键时,keys() 方法不会忽略缺失属性的键。
    • 当读取类数组对象的键时,keys() 方法会读取 thislength 属性,生成 0length - 1 之间的所有整数索引。实际并不会访问索引。
    let arr = ['a', 'b', 'c']
    console.log([...arr.keys()]) // [0, 1, 2]
    
    let sparseArr = ['a', , 'c']
    let iterator = sparseArr.keys()
    
    console.log(iterator.next()) // { value: 0, done: false }
    console.log(iterator.next()) // { value: 1, done: false }
    console.log(iterator.next()) // { value: 2, done: false }
    console.log(iterator.next()) // { value: undefined, done: true }
    
    console.log([...sparseArr.keys()]) // [0, 1, 2]
    console.log(Object.keys(sparseArr)) // ['0', '2']
    
    const arrayLike = {
      length: 3,
    }
    console.log([...Array.prototype.keys.call(arrayLike)])
    // [0, 1, 2]
    
  • Array.prototype.values()

    语法: arr.values()

    描述: 返回一个新的数组迭代器对象,该对象迭代数组中每个元素的值。

    • 当读取稀疏数组的键时,values() 方法会将空槽作为 undefined 迭代。
    • 当读取类数组对象的键时,values() 方法会读取 thislength 属性,然后访问每个整数索引。
    let arr = ['a', 'b', 'c']
    console.log([...arr.values()]) // ['a', 'b', 'c']
    
    let sparseArr = ['a', , 'c']
    let iterator = sparseArr.values()
    
    console.log(iterator.next()) // { value: 'a', done: false }
    console.log(iterator.next()) // { value: undefined, done: false }
    console.log(iterator.next()) // { value: 'c', done: false }
    console.log(iterator.next()) // { value: undefined, done: true }
    
    console.log([...sparseArr.values()]) // ['a', undefined, 'c']
    console.log(Object.values(sparseArr)) // ['a', 'c']
    
    const arrayLike = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3,
    }
    console.log([...Array.prototype.values.call(arrayLike)])
    // ['a', 'b', 'c']
    
  • Array.prototype.entries()

    语法: arr.entries()

    描述: 返回一个新的数组迭代器对象,该对象包含数组中每个索引的键/值对。

    • 当读取稀疏数组的键时,entries() 方法不会忽略缺失属性的键,会将空槽的值作为 undefined
    • 当读取类数组对象的键时,entries() 方法会读取 thislength 属性,然后访问每个整数索引。
    let arr = ['a', 'b', 'c']
    console.log([...arr.entries()]) // [[0, 'a'], [1, 'b'], [2, 'c']]
    
    let sparseArr = ['a', , 'c']
    let iterator = sparseArr.entries()
    
    console.log(iterator.next()) // { value: [0, 'a'], done: false }
    console.log(iterator.next()) // { value: [1, undefined], done: false }
    console.log(iterator.next()) // { value: [2, 'c'], done: false }
    console.log(iterator.next()) // { value: undefined, done: true }
    
    console.log([...sparseArr.entries()]) // [[0, 'a'], [1, undefined], [2, 'c']]
    console.log(Object.entries(sparseArr)) // [['0', 'a'], ['2', 'c']]
    
    const arrayLike = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3,
    }
    console.log([...Array.prototype.entries.call(arrayLike)])
    // [[0, 'a'], [1, 'b'], [2, 'c']]
    

复制和填充方法

  • Array.prototype.copyWithin()

    语法: arr.copyWithin(target, start, end)

    描述: 浅拷贝数组的一部分到同一数组中的另一个位置,并返回它。

    • 不会改变原数组的长度,会改变原数组。
    • 如果复制的区域是稀疏的(含有空元素 empty),原来的 empty 元素会被删除并替换为拷贝的 empty 元素。
    • 当读取类数组对象的键时,copyWithin() 方法会读取 thislength 属性,然后操作所涉及的整数索引。

    参数:

    • target :序列开始替换的目标位置,以 0 为起始的下标,且将被转换为整数。
      • 如果 target < 0 ,则从数组末尾开始计数(即:target + arr.length)。
      • 如果 target < -arr.length,则使用 0
      • 如果 target >= array.length,则不会拷贝任何内容。
      • 如果 target 位于 start 之后,则复制只会持续到 array.length 结束(即:copyWithin() 永远不会扩展数组)。
    • start :可选值。要复制的元素序列的起始位置,以 0 为起始的下标表示,且将被转换为整数。
      • 如果 start < 0 ,将从数组末尾开始计数(即:start + arr.length)。
      • 如果 start < -arr.lengthstart 省略,则默认为 0
      • 如果 start >= arr.length,则不会拷贝任何内容。
    • end :可选值。要复制的元素序列的结束位置(不包含该位置),以 0 为起始的下标表示,且将被转换为整数。
      • 如果 end < 0 ,将从数组末尾开始计数(即:end + arr.length)。
      • 如果 end < -arr.length,则使用 0
      • 如果 end >= arr.lengthend 省略,则默认为 arr.length,会导致直到数组末尾的所有元素都被复制。
      • 如果 end 位于 start 之前,则不会拷贝任何内容。
    // 将数组中从下标 0 到数组结束的元素(包含)复制到从下标 3 开始的位置
    [1, 2, 3, 4, 5].copyWithin(-2) // [1, 2, 3, 1, 2]
    
    // 将数组中从下标 3 到数组结束的元素(包含)复制到从下标 0 开始的位置
    [1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]
    
    // 将数组中从下标 3 到下标 4 (不包含) 的元素复制到从下标 0 开始的位置
    [1, 2, 3, 4, 5].copyWithin(0, 3, 4) // [4, 2, 3, 4, 5]
    
    // 将数组中从下标 2 到下标 4 (不包含)的元素复制到从下标 3 开始的位置
    [1, 2, 3, 4, 5].copyWithin(-2, -3, -1) // [1, 2, 3, 3, 4]
    
    // 将数组中从下标 1 到下标 2 (不包含)的元素复制到从下标 2 开始的位置
    [1, , 3].copyWithin(2, 1, 2) // [1, empty × 2]
    
    [].copyWithin.call({ length: 5, 3: 1 }, 0, 3)
    // { 0: 1, 3: 1, length: 5 }
    
  • Array.prototype.fill()

    语法: arr.fill(value, start, end)

    描述: 用一个固定值 value 填充一个数组中从起始索引 start (默认为 0)到终止索引 end (默认为 arr.length,不包含)内的全部元素,并返回修改后的数组。

    • 不会改变原数组的长度,会改变原数组。
    • 对于稀疏数组,会使用 value 填充稀疏数组的空槽(empty 元素)。
    • 对于类数组对象,读取 thislength 属性,并设置从 startend 的每个整数属性的值。

    参数:

    • value :用来填充数组元素的值。如果 value 是个对象,数组中每一项都会引用这个元素。
    • start :基于零的索引,从此开始填充。会被转为整数。
      • 如果 start < 0 ,则从数组末尾开始计数(即:start + arr.length)。
      • 如果 start < -arr.lengthstart 被省略,则默认为 0
      • 如果 start >= arr.length ,则没有索引被填充。
    • end :基于零的索引,在此结束填充,但不包含 end 索引。会被转为整数。
      • 如果 end < 0 ,则从数组末尾开始计数(即:end + arr.length)。
      • 如果 end < -arr.lengthend 被省略,则默认为 0
      • 如果 end >= arr.length ,则使用 arr.length ,导致所有索引被填充。
      • 如果 end 的位置在 start 之前,则没有索引被填充。
    [1, 2, 3].fill(4) // [4, 4, 4]
    [1, 2, 3].fill(4, 1) // [1, 4, 4]
    [1, 2, 3].fill(4, 1, 2) // [1, 4, 3]
    [1, 2, 3].fill(4, 1, 1) // [1, 2, 3]
    [1, 2, 3].fill(4, 3, 3) // [1, 2, 3]
    [1, 2, 3].fill(4, -3, -2) // [1, 2, 3]
    [1, 2, 3].fill(4, NaN, NaN) // [1, 2, 3]
    [1, 2, 3].fill(4, NaN, NaN) // [1, 2, 3]
    [1, 2, 3].fill(4, 3, 5) // [1, 2, 3]
    
    Array(3).fill(4) // [4, 4, 4]
    
    let fillFooArr = Array(3).fill({ foo: 'foo' })
    // [{ foo: 'foo' }, { foo: 'foo' }, { foo: 'foo' }]
    fillFooArr[0].foo = 'bar'
    // [{ foo: 'bar' }, { foo: 'bar' }, { foo: 'bar' }]
    
    let arrayLike = { length: 2 }
    Array.prototype.fill.call(arrayLike, 1) // { 0: 1, 1: 1, length: 2 }
    

转换方法

  • Array.prototype.join()

    语法: arr.join(separator)

    描述: 将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回,用逗号或指定的分隔符字符串分隔。

    • 如果 arr.length0,则返回空字符串('')。
    • 如果 arr.length1,则返回该元素而不使用分隔符。
    • 如果一个元素是 undefinednull,会被转换为空字符串(''),而不是字符串 'undefined''null'
    • 对于稀疏数组,迭代时会把空槽(empty 元素)当作 undefined 处理。
    • 对于类数组对象,会读取 thislength 属性,然后访问每个整数索引。

    参数:

    • separator :可选值。指定一个字符串来分隔数组的每个元素。
      • 如果省略,数组元素用逗号(,)分隔。
      • 如果是空字符串(''),则所有元素之间都没有任何字符。
  • Array.prototype.toLocaleString()

    语法: arr.toLocaleString(locales, options)

    描述: 返回一个字符串,表示数组中的所有元素。每个元素通过调用自己的 toLocaleString() 方法转换为字符串,并且使用分隔符(例如逗号 , ,取决于当前的语言环境)将转换后的字符串拼接起来。

    • 相关元素调用的 toLocaleString() 方法:
      • ObjectObject.prototype.toLocaleString()
      • NumberNumber.prototype.toLocaleString()
      • DateDate.prototype.toLocaleString()
    • 如果元素是 undefinednull,会被转化为空字符串 ''
    • 对于稀疏数组,迭代时会把空槽(empty 元素)当作 undefined 处理。
    • 对于类数组对象,会读取 thislength 属性,然后访问每个整数索引。
    const arr = [1, 'foo', undefined, null, , { a: 1 }]
    arr.join() // '1,foo,,,,[object Object]'
    arr.join('') // '1foo[object Object]'
    arr.join('+') // '1+foo++++[object Object]'
    
    // 稀疏数组
    [1,,3].join() // '1,,3'
    
    // 类数组对象
    const arrayLike = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3,
    }
    Array.prototype.join.call(arrayLike) // 'a,b,c'
    

    参数:

    • locales :可选值。带有 BCP 47 语言标签的字符串,或者此类字符串的数组。
    • options :可选值。一个具有配置属性的对象。
    let arr = [
      1,
      'foo',
      undefined,
      null,
      ,
      { a: 1 },
      new Set(),
      new Map(),
      new Date(),
    ]
    arr.toLocaleString() 
    // '1,foo,,,,[object Object],[object Set],[object Map],2023/8/13 23:42:27'
    
    // 稀疏数组
    [1, , 3].toLocaleString() // '1,,3'
    
    // 类数组对象
    const arrayLike = {
      0: 1,
      1: 2,
      2: 3,
      length: 3,
    }
    Array.prototype.toLocaleString.call(arrayLike) // '1,2,3'
    
  • Array.prototype.toString()

    语法: arr.toString()

    描述: 返回一个字符串,表示指定的数组及其元素。

    • 数组的 toString() 方法实际上在内部调用了 join() 方法来拼接数组并返回一个包含所有数组元素的字符串,元素之间用逗号(',')分隔。
    • 如果 join() 方法不可用或者不是函数,则会使用 Object.prototype.toString 来代替,并返回 [object Array]
    • 如果元素是 undefinednull,会被转化为空字符串 ''
    • 对于稀疏数组,迭代时会把空槽(empty 元素)当作 undefined 并生成一个额外的分隔符。
    • 对于类数组对象,期望 this 具有 join() 方法;如果不存在,则使用 Object.prototype.toString()
    let arr = [
      1,
      'foo',
      undefined,
      null,
      ,
      { a: 1 },
      new Set(),
      new Map(),
      new Date(),
    ]
    arr.toString() // '1,foo,,,,[object Object],[object Set],[object Map],2023/8/13 23:50:47'
    
    // 稀疏数组
    [1, , 3].toString() // '1,,3'
    
    // 类数组对象
    Array.prototype.toString.call({ join: () => 1 }) // 1
    Array.prototype.toString.call({ join: () => undefined }) // undefined
    Array.prototype.toString.call({ join: 'not function' }) // '[object Object]'
    

搜索与位置查找方法

  • Array.prototype.includes()

    语法: arr.includes(searchElement, fromIndex)

    描述: 判断一个数组是否包含一个指定的值。如果包含(或者如果指定了 fromIndex ,找到 searchElement 值)则返回 true,否则返回 false

    • 该方法使用 零值相等 算法将 searchElement 与数组中的元素进行比较。

      • 0-0+0 都被认为是相等的。
      • false 不被认为与 0 相等。
      • NaN 可以被正确的搜索到。

      零值相等与严格相等的区别在于其将 NaN 视作是相等的,与同值相等的区别在于其将 -00 视作相等的。

      // 零值相等不作为 JavaScript API 公开,但可以通过自定义代码实现
      function sameValueZero(x, y) {
        if (typeof x === 'number' && typeof y === 'number') {
          // x 和 y 相等(可能是 -0 和 0)或它们都是 NaN
          return x === y || (x !== x && y !== y)
        }
        return x === y
      }
      
    • 对于稀疏数组,迭代时会把空槽(empty 元素)当作 undefined 处理。

    • 对于类数组对象,会读取 thislength 属性,然后访问每个整数索引。

    参数:

    • searchElement :需要查找的值。
    • fromIndex :可选值。开始搜索的索引(从零开始)。
      • 如果 fromIndex < 0,则从数组末尾开始计数(即 fromIndex + array.length)。
      • 如果 fromIndex < -array.length 或者省略 fromIndex,则使用 0,会导致整个数组被搜索。
      • 如果 fromIndex >= array.length,则不会搜索数组并返回 false
    [1, 2, 3].includes(2) // true
    ['1', '2', '3'].includes(3) // false
    
    [1, 2, 3].includes(3, 3) // false
    [1, 2, 3].includes(3, -1) // true
    [1, 2, 3].includes(3, -100) // true
    
    [1, 2, NaN].includes(NaN) // true
    [0, 2, 3].includes(+0) // true
    
    // 稀疏数组
    [1, , 3].includes(undefined) // true
    
    // 类数组对象
    const arrayLike = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3,
    }
    Array.prototype.includes.call(arrayLike, 'b') // true
    
  • Array.prototype.indexOf()

    语法: arr.indexOf(searchElement, fromIndex)

    描述: 返回数组中第一次出现给定元素的下标,如果不存在则返回 -1

    • 该方法使用 严格相等(===) 算法将 searchElement 与数组中的元素进行比较。NaN 值永远不会被比较为相等。
    • 对于稀疏数组,迭代时会把跳过空槽empty 元素)。
    • 对于类数组对象,会读取 thislength 属性,然后访问每个整数索引。

    参数:

    • searchElement :需要查找的值。
    • fromIndex :可选值。开始搜索的索引(从零开始)。
      • 如果 fromIndex < 0,则从数组末尾开始计数(即 fromIndex + array.length)。
      • 如果 fromIndex < -array.length 或者省略 fromIndex,则使用 0,会导致整个数组被搜索。
      • 如果 fromIndex >= array.length,则不会搜索数组并返回 -1
    let arr = [1, 2, 2, , NaN]
    arr.indexOf(1) // 0
    arr.indexOf(1, -5) // 0
    arr.indexOf(2) // 1
    arr.indexOf(2, 2) // 2
    arr.indexOf(3) // -1
    arr.indexOf(NaN) // -1
    
    // 稀疏数组
    [1, , 3].indexOf(undefined) // -1
    
    // 类数组对象
    const arrayLike = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3,
    }
    Array.prototype.indexOf.call(arrayLike, 'b') // 1
    
  • Array.prototype.lastIndexOf()

    语法: arr.lastIndexOf(searchElement, fromIndex)

    描述: 返回数组中最后一次出现给定元素的索引,如果不存在则返回 -1。该方法从 fromIndex 开始向前搜索数组。

    • 该方法使用 严格相等(===) 算法将 searchElement 与数组中的元素进行比较。NaN 值永远不会被比较为相等。
    • 对于稀疏数组,迭代时会把跳过空槽empty 元素)。
    • 对于类数组对象,会读取 thislength 属性,然后访问每个整数索引。

    参数:

    • searchElement :需要查找的值。
    • fromIndex :可选值。反向搜索的起始位置(从零开始)。
      • 如果 fromIndex < 0,则从数组末尾开始计数(即 fromIndex + array.length)。
      • 如果 fromIndex < -array.length ,则不搜索数组并返回 -1
      • 如果 fromIndex >= array.length 或者省略 fromIndex,则使用 array.length - 1,会导致整个数组被搜索。
    let arr = [1, 2, 2, , NaN]
    arr.lastIndexOf(1) // 0
    arr.lastIndexOf(1, -5) // 0
    arr.lastIndexOf(2) // 2
    arr.lastIndexOf(2, 2) // 2
    arr.lastIndexOf(2, 1) // 1
    arr.lastIndexOf(3) // -1
    arr.lastIndexOf(NaN) // -1
    
    // 稀疏数组
    [1, , 3].lastIndexOf(undefined) // -1
    
    // 类数组对象
    const arrayLike = {
      0: 'a',
      1: 'b',
      2: 'b',
      length: 3,
    }
    Array.prototype.lastIndexOf.call(arrayLike, 'b') // 2
    
  • Array.prototype.find()

    语法: arr.find(callbackFn, thisArg)

    描述: 返回数组中满足提供的 callbackFn 函数的第一个元素的值,否则返回 undefined

    • 不会改变原数组,提供 callbackFn 的函数可以更改原数组。
    • 在第一次调用 callbackFn 之前,数组的长度会被保存。
      • callbackFn 不会访问超出数组初始长度的任何元素
      • 已经访问过的索引的更改,不会导致再次在这些元素上调用 callbackFn
      • 如果 callbackFn 更改了数组中现有的、尚未访问的元素,传递给 callbackFn 的值将是该元素被访问时的值。被删除的元素被视为 undefined
    • 对于稀疏数组,未赋值的空槽(empty 元素)当作 undefined 处理。
    • 对于类数组对象,会读取 thislength 属性,然后访问每个整数索引。

    参数:

    • callbackFn :数组中的每个元素执行的回调函数,该函数应该返回一个 Truthy(真值)来表示已经找到了匹配的元素。该函数接收以下参数:
      • element :数组中当前正在处理的元素。
      • index :正在处理的元素在数组中的索引。
      • array :调用了 find() 的数组本身。
    • thisArg :执行 callbackFn 时用作 this 的值。
    const inventory = [
      { name: 'apples', quantity: 2 },
      { name: 'bananas', quantity: 0 },
    ]
    inventory.find(({ name }) => name === 'bananas')
    // { name: 'bananas', quantity: 0 }
    
    // 稀疏数组
    [0, 1, , , , 5].find((item) => item === undefined)
    // undefined
    
    // 类数组对象
    const arrayLike = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3,
    }
    Array.prototype.find.call(arrayLike, (x) => !Number.isInteger(x))
    // 'a'
    
  • Array.prototype.findIndex()

    语法: arr.findIndex(callbackFn, thisArg)

    描述: 返回数组中满足提供的 callbackFn 函数的第一个元素的索引。若没有找到对应元素则返回 -1

    • 不会改变原数组,提供 callbackFn 的函数可以更改原数组。
    • 在第一次调用 callbackFn 之前,数组的长度会被保存。
      • callbackFn 不会访问超出数组初始长度的任何元素
      • 已经访问过的索引的更改,不会导致再次在这些元素上调用 callbackFn
      • 如果 callbackFn 更改了数组中现有的、尚未访问的元素,传递给 callbackFn 的值将是该元素被访问时的值。被删除的元素被视为 undefined
    • 对于稀疏数组,未赋值的空槽(empty 元素)当作 undefined 处理。
    • 对于类数组对象,会读取 thislength 属性,然后访问每个整数索引。

    参数:

    • callbackFn :数组中的每个元素执行的回调函数,该函数应该返回一个 Truthy(真值)来表示已经找到了匹配的元素。该函数接收以下参数:
      • element :数组中当前正在处理的元素。
      • index :正在处理的元素在数组中的索引。
      • array :调用了 findIndex() 的数组本身。
    • thisArg :执行 callbackFn 时用作 this 的值。
    const inventory = [
      { name: 'apples', quantity: 2 },
      { name: 'bananas', quantity: 0 },
    ]
    inventory.findIndex(({ name }) => name === 'bananas') // 1
    
    // 稀疏数组
    [0, 1, , , , 5].findIndex((item) => item === undefined) // 2
    
    // 类数组对象
    const arrayLike = {
      0: 0,
      'foo': 'fooVal',
      1: 7.3,
      2: 2,
      length: 3,
    }
    Array.prototype.findIndex.call(arrayLike, (x) => !Number.isInteger(x))
    // 1
    
  • Array.prototype.findLast()

    语法: arr.findLast(callbackFn, thisArg)

    描述: 反向迭代数组,并返回满足提供的 callbackFn 函数的第一个元素的值。如果没有找到对应元素,则返回 undefined

    • 不会改变被调用的数组,提供 callbackFn 的函数可以更改原数组。
    • 在第一次调用 callbackFn 之前,数组的长度会被保存。
      • callbackFn 不会访问超出数组初始长度的任何元素
      • 对于已经访问过的索引重新赋值不会再次调用 callbackFn
      • 如果 callbackFn 更改了数组中现有的、尚未访问的元素,传递给 callbackFn 的值将是该元素被访问时的值。被删除的元素被视为 undefined
    • 对于稀疏数组,未赋值的空槽(empty 元素)当作 undefined 处理。
    • 对于类数组对象,会读取 thislength 属性,然后访问每个整数索引。

    参数:

    • callbackFn :数组中的每个元素执行的回调函数,该函数应该返回一个 Truthy(真值)来表示已经找到了匹配的元素。该函数接收以下参数:
      • element :数组中当前正在处理的元素。
      • index :正在处理的元素在数组中的索引。
      • array :调用了 findLast() 的数组本身。
    • thisArg :执行 callbackFn 时用作 this 的值。
    const inventory = [
      { name: 'apples', quantity: 2 },
      { name: 'bananas', quantity: 0 },
      { name: "cherries", quantity: 5 },
    ]
    inventory.findLast(({ name }) => name === 'bananas')
    // {name: 'bananas', quantity: 0}
    
    // 稀疏数组
    [0, 1, , , , 5].findLast((item) => item === undefined) // undefined
    
    // 类数组对象
    const arrayLike = {
      0: 0,
      'foo': 'fooVal',
      1: 7.3,
      2: 2,
      length: 3,
    }
    Array.prototype.findLast.call(arrayLike, (x) => Number.isInteger(x))
    // '2'
    
  • Array.prototype.findLastIndex()

    语法: arr.findLastIndex(callbackFn, thisArg)

    描述: 反向迭代数组,并返回满足提供的 callbackFn 函数的第一个元素的索引。如果没有找到对应元素,则返回 undefined

    • 不会改变被调用的数组,提供 callbackFn 的函数可以更改原数组。
    • 在第一次调用 callbackFn 之前,数组的长度会被保存。
      • callbackFn 不会访问超出数组初始长度的任何元素
      • 对于已经访问过的索引重新赋值不会再次调用 callbackFn
      • 如果 callbackFn 更改了数组中现有的、尚未访问的元素,传递给 callbackFn 的值将是该元素被访问时的值。被删除的元素被视为 undefined
    • 对于稀疏数组,未赋值的空槽(empty 元素)当作 undefined 处理。
    • 对于类数组对象,会读取 thislength 属性,然后访问每个整数索引。

    参数:

    • callbackFn :数组中的每个元素执行的回调函数,该函数应该返回一个 Truthy(真值)来表示已经找到了匹配的元素。该函数接收以下参数:
      • element :数组中当前正在处理的元素。
      • index :正在处理的元素在数组中的索引。
      • array :调用了 findLastIndex() 的数组本身。
    • thisArg :执行 callbackFn 时用作 this 的值。
    const inventory = [
      { name: 'apples', quantity: 2 },
      { name: 'bananas', quantity: 0 },
      { name: "cherries", quantity: 5 },
    ]
    inventory.findLastIndex(({ name }) => name === 'bananas') // 1
    
    // 稀疏数组
    [0, 1, , , , 5].findLastIndex((item) => item === undefined) // 4
    
    // 类数组对象
    const arrayLike = {
      0: 0,
      'foo': 'fooVal',
      1: 7.3,
      2: 2,
      length: 3,
    }
    Array.prototype.findLastIndex.call(arrayLike, (x) => Number.isInteger(x))
    // '2'
    

添加、删除、替换方法

  • Array.prototype.concat()

    语法: arr.concat(value0, value1, /* … ,*/ valueN)

    描述: 用于合并两个或多个数组。不会更改现有数组,而是返回一个新数组

    • 不会改变原数组的长度,不会改变原数组,会返回一个新的浅拷贝数组。
    • 对于稀疏数组,会保留空槽(empty 元素)。
    • 对于类数组对象
      • 如果 this 值不是数组,它会被转换为一个对象,然后以与 concat() 的参数相同的方式处理。
      • 仅在 Symbol.isConcatSpreadable 被设置为真值(例如,true)时,才会将类数组对象视作数组。
    let a = [1, 2]
    let b = ['a', 'b']
    let c = ['x', 'y']
    a.concat(b, c) // [1, 2, 'a', 'b', 'x', 'y']
    
    // 稀疏数组
    [1, , 3].concat([4, 5]) // [1, empty, 3, 4, 5]
    
    // 类数组对象
    const arrayLike01 = {
      0: 'a',
      1: 'b',
      length: 2,
    }
    Array.prototype.concat.call(arrayLike01, 2, 3)
    // [{ 0: 'a', 1: 'b', length: 2 }, 2, 3]
    
    const arrayLike02 = {
      0: 'a',
      1: 'b',
      length: 2,
      [Symbol.isConcatSpreadable]: true,
    }
    Array.prototype.concat.call(arrayLike02, 2, 3)
    // ['a', 'b', 2, 3]
    
  • Array.prototype.push()

    语法: arr.push(element0, element1, /* … ,*/ elementN)

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

    • 会改变原数组的长度,会改变原数组,返回新的数组长度。
    • 对于类数组对象:
      • 首先,会读取 thislength 属性。
      • 然后,它将 this 的每个索引从 length 开始设置,并将参数传递给 push()
      • 最后,它将 length 设置成之前的长度加上已添加元素的数量。
    let arr1 = [1, 2]
    arr1.push('a', 'b') // 返回新的数组长度为 4
    console.log(arr1) // [1, 2, 'a', 'b']
    
    let arr2 = ['x', 'y']
    arr2.push(...arr1) // 返回新的数组长度为 6
    console.log(arr2) // ['x', 'y', 1, 2, 'a', 'b']
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      unrelated: 'foo',
      2: 4,
    }
    Array.prototype.push.call(arrayLike, 1, 2) // 返回新的数组长度为 5
    console.log(arrayLike)// {'2': 4, '3': 1, '4': 2, length: 5, unrelated: 'foo'}
    
  • Array.prototype.pop()

    语法: arr.pop()

    描述: 从数组中删除最后一个元素,并返回该元素的值(当数组为空时返回 undefined)。

    • 会改变原数组的长度,会改变原数组。
    • 对于类数组对象,会读取 this 上的 length 属性。如果 length 属性为不存在、负数、 undefined 或者 0,会被再次设置为 0 。否则,返回并删除位于 length - 1 处的属性。
    let arr1 = ['a', 'b', 'c']
    arr1.pop() // 返回被删除的元素为 'c'
    console.log(arr1) // ['a', 'b']
    
    const arrayLike = {
      length: 3,
      unrelated: 'foo',
      2: 'b',
    }
    Array.prototype.pop.call(arrayLike) // 返回被删除的元素为 'b'
    console.log(arrayLike) // { length: 2, unrelated: 'foo' }
    
    const plainObj = {}
    Array.prototype.pop.call(plainObj) // 返回被删除的元素为 undefined
    console.log(plainObj) // { length: 0 }
    
  • Array.prototype.shift()

    语法: arr.shift()

    描述: 从数组中删除第一个元素,并返回该元素的值。(当数组为空时返回 undefined)。

    • 会改变原数组的长度,会改变原数组。
    • 对于类数组对象,会读取 this 上的 length 属性。如果 length 属性为不存在、负数、 undefined 或者 0,会被再次设置为 0 。否则,返回 0 处的属性,其余属性向左移动 1length 属性递减 1
    let arr1 = ['a', 'b', 'c']
    arr1.shift() // 返回被删除的元素为 'a'
    console.log(arr1) // ['b', 'c']
    
    const arrayLike = {
      length: 3,
      unrelated: 'foo',
      2: 'b',
    }
    Array.prototype.shift.call(arrayLike) // 返回被删除的元素为 undefined ,因为是一个空槽
    console.log(arrayLike) // {1: 'b', length: 2, unrelated: 'foo'}
    
    const plainObj = {}
    Array.prototype.shift.call(plainObj) // 返回被删除的元素为 undefined
    console.log(plainObj) // { length: 0 }
    
  • Array.prototype.unshift()

    语法: arr.unshift(element0, element1, /* … ,*/ elementN)

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

    • 会改变原数组的长度,会改变原数组。
    • 对于类数组对象:
      • 首先,会读取 thislength 属性。
      • 然后,它将 0length - 1 范围内的所有属性按参数数量右移,并将每个索引从 0 开始,并将参数传递给 unshift()
      • 最后,它将 length 设置为之前的长度加上前置元素的数量。
    let arr1 = ['a', 'b', 'c']
    arr1.unshift('x') // 返回数组长度为 4
    console.log(arr1) // ['x', 'a', 'b', 'c']
    
    const arrayLike = {
      length: 3,
      unrelated: 'foo',
      2: 'b',
    }
    Array.prototype.unshift.call(arrayLike, 1, 2) // 返回数组长度为 5
    console.log(arrayLike) // { 0: 1, 1: 2, 4: 'b', length: 5, unrelated: 'foo' }
    
    const plainObj = {}
    Array.prototype.unshift.call(plainObj, 1, 2) // 返回数组长度为 2
    console.log(plainObj) // { 0: 1, 1: 2, length: 2 }
    
  • Array.prototype.splice()

    语法: arr.splice(start, deleteCount, item1, ..., itemN)

    描述: 从给定索引(start)开始移除指定数量(deleteCount)元素,可以在相同索引处插入给定元素(item1, ..., itemN),返回包含了删除元素的数组

    • 会改变原数组的长度,会改变原数组,返回包含了删除元素的数组。
    • 对于稀疏数组,如果删除的部分是空槽(empty 元素),则返回的数组也是空槽(empty 元素)。
    • 对于类数组对象,会读取 thislength 属性,会读取所需的整数键属性并将其写入新数组。

    参数:

    • start :表示要开始改变数组的位置,从 0 开始索引,会被转换为整数。
      • 如果 start < 0,使用 start + array.length。
      • 如果 start < 0,则从数组末尾开始计数(即 start + array.length)。
      • 如果 start < -array.length ,则使用 0
      • 如果 start >= array.length ,则不会删除任何元素,该方法表现为添加所提供的元素。
      • 如果 start 被省略(即,调用 splice() 时,不传参),则不会删除任何元素。
      • 如果 startundefined,会被转换为 0
    • deleteCount :可选值。表示从 start 开始删除的元素数量。
      • 如果 deleteCount 被省略,或者其值大于或者等于 start 指定位置到数组末尾的元素数量,从 start 到数组末尾的所有元素将被删除。
      • 如果 deleteCount0 或者负数,则不会删除元素。
      • 如果需要传递任何 itemN 参数,则应该设置 deleteCountInfinity 值,以删除 start 之后的所有元素。显示的设置为 undefined,会被转换为 0
    • item1, …, itemN :可选值。从 start 开始要加入到数组中的元素。如果不指定任何元素,splice() 将只从数组中删除元素。
    let arr = ['a', 'b', 'c']
    
    // 从给定索引 2 开始移除 0 个元素,并插入 'd'
    arr.splice(2, 0, 'd') // 返回删除元素的数组为 []
    console.log(arr) // ['a', 'b', 'd', 'c']
    
    // 从给定索引 3 开始移除 1 个元素
    arr.splice(3, 1) // 返回删除元素的数组为 ['c']
    console.log(arr) // ['a', 'b', 'd']
    
    // 从给定索引 2 开始移除 1 个元素,并插入 'c'
    arr.splice(2, 1, 'c') // 返回删除元素的数组为 ['d']
    console.log(arr) // ['a', 'b', 'c']
    
    // 从给定索引 -2 (即,索引为 1)开始移除 1 个元素
    arr.splice(-2, 1) // 返回删除元素的数组为 ['b']
    console.log(arr) // ['a', 'c']
    
    // 从给定索引 1 开始移除之后的所有元素
    arr.splice(1) // 返回删除元素的数组为 ['c']
    console.log(arr) // ['a']
    
    // 稀疏数组
    let sparseArr = [1, , 3, 4, , 6]
    // 从给定索引 1 开始移除 2 个元素
    sparseArr.splice(1, 2) // 返回删除元素的数组为 [empty, 3]
    console.log(sparseArr) // [1, 4, , 6]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      unrelated: 'foo',
      0: 5,
      2: 4,
    }
    Array.prototype.splice.call(arrayLike, 0, 1, 2, 3) // 返回删除元素的数组为 [5]
    console.log(arrayLike) // { 0: 2, 1: 3, 3: 4, length: 4, unrelated: 'foo' }
    
  • Array.prototype.toSpliced()

    语法: arr.toSpliced(start, deleteCount, item1, ..., itemN)

    描述: 从给定索引(start)开始移除指定数量(deleteCount)元素,可以在相同索引处插入给定元素(item1, ..., itemN),返回一个新数组

    • splice() 方法对应的复制版本。
    • 不会改变原数组的长度,不会改变原数组,会返回一个新的浅拷贝数组。
    • 对于稀疏数组,原数组的空槽(empty 元素),在新数组中被替换成 undefined
    let arr = ['a', 'b', 'c']
    
    // 从给定索引 2 开始移除 0 个元素,并插入 'd'
    arr.toSpliced(2, 0, 'd') // 返回新数组为 ['a', 'b', 'd', 'c']
    
    // 从给定索引 3 开始移除 1 个元素
    arr.toSpliced(3, 1) // 返回新数组为 ['a', 'b', 'd']
    
    // 从给定索引 2 开始移除 1 个元素,并插入 'c'
    arr.toSpliced(2, 1, 'c') // 返回新数组为 ['a', 'b', 'c']
    
    // 从给定索引 -2 (即,索引为 1)开始移除 1 个元素
    arr.toSpliced(-2, 1) // 返回新数组为 ['a', 'c']
    
    // 从给定索引 1 开始移除之后的所有元素
    arr.toSpliced(1) // 返回新数组为 ['a']
    
    // 稀疏数组
    let sparseArr = [1, , 3, 4, , 6]
    // 从给定索引 1 开始移除 2 个元素
    sparseArr.toSpliced(1, 2) // 返回新数组为  [1, 4, undefined, 6]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      unrelated: 'foo',
      0: 5,
      2: 4,
    }
    Array.prototype.toSpliced.call(arrayLike, 0, 1, 2, 3) 
    // 返回新数组为 [2, 3, undefined, 4]
    

数组元素提取与替换

  • Array.prototype.at()

    语法: arr.at(index)

    描述: 返回数组中给定索引匹配 index 的元素。index 为负数则从数组中的最后一个元素开始倒数。

    • 如果 index < -arr.lengthindex >= arr.length,则返回 undefined
    let arr = ['a', 'b', 'c']
    
    arr.at(0) // 'a'
    arr.at(-1) // 'c'
    
    // 稀疏数组
    let sparseArr = [1, , 3, 4, , 6]
    sparseArr.at(1) // undefined
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      unrelated: 'foo',
      0: 'a',
      2: 'b',
    }
    Array.prototype.at.call(arrayLike, -1) // 'b'
    
  • Array.prototype.slice()

    语法: arr.slice(start, end)

    描述: 返回一个新的数组对象,包含从索引 startend(不包含) 的原数组的元素的浅拷贝

    • 原始数组不会被改变,会返回一个新的浅拷贝数组。
    • 对于稀疏数组,如果提取的部分是空槽(empty 元素),则返回的数组也是空槽(empty 元素)。
    • 对于类数组对象,会读取 thislength 属性,然后从 startend 读取整数键属性,并将它们定义在一个新创建的数组中。

    参数:

    • start :提取起始处的索引(从 0 开始),会转换为整数。
      • 如果 start < 0 ,则从数组末尾开始计数(即 start + array.length)。
      • 如果 start < -arr.length 或者 start 省略,则使用 0
      • 如果 start >= arr.length,则不提取任何元素。
    • end :提取终止处的索引(从 0 开始),会转换为整数。提取不包含 end 位置的元素。
      • 如果 end < 0 ,则从数组末尾开始计数(即 end + array.length)。
      • 如果 end < -arr.length 或者 end 省略,则使用 0
      • 如果 end >= arr.length,则使用 arr.length,提取所有元素直到末尾。
      • 如果 end <= start,则不提取任何元素。
    let arr1 = ['a', 'b', 'c']
    arr1.slice(0) // ['a', 'b', 'c']
    arr1.slice(3) // []
    arr1.slice(-3) // ['b', 'c']
    arr1.slice(0, 1) // ['a']
    arr1.slice(0, -2) // ['a']
    arr1.slice(-2, -2) // []
    
    let arr2 = [{ color: 'red' }, 'foo', 1]
    let arr3 = arr2.slice(0, 1) // [{ color: 'red' }]
    arr3[0].color = 'yellow'
    console.log(arr2) // [{ color: 'yellow' }, 'foo', 1]
    console.log(arr3) // [{ color: 'yellow' }]
    
    // 稀疏数组
    [1, , 3, 4, , 6].slice(1, 3) // [empty, 3]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      unrelsliceed: 'foo',
      0: 'a',
      2: 'b',
    }
    Array.prototype.slice.call(arrayLike, 1, 3) // [empty, 'b']
    
  • Array.prototype.with()

    语法: arr.with(index, value)

    描述: 返回一个指定索引 index 处的值被新值 value 替换的新数组

    • 原始数组不会被改变,会返回一个新的浅拷贝数组。
    • 对于稀疏数组,原数组的空槽(empty 元素),在新数组中被替换成 undefined
    • 对于类数组对象,会读取 thislength 属性,之后读取 this 上的每个整数键并写入到新数组中,同时 value 会被写入指定的 index

    参数:

    • index :要修改的数组索引(从 0 开始),将会转换为整数。
      • 如果 index < 0 ,则从数组末尾开始计数(即 index + array.length)。
      • 如果规范化后的索引超出数组边界(index > arr.lengthindex < -arr.length),会抛出 RangeError
    • value :要分配给指定索引的任何值。
    let arr1 = ['a', 'b', 'c']
    arr1.with() // [undefined, 'b', 'c']
    arr1.with(0, 'x') // ['x', 'b', 'c']
    
    let arr2 = [{ color: 'red' }, 'foo', 1]
    let arr3 = arr2.with(1, 'bar') // [{ color: 'red' }]
    arr3[0].color = 'yellow'
    console.log(arr2) // [{ color: 'yellow' }, 'foo', 1]
    console.log(arr3) // [{ color: 'yellow' }, 'bar', 1]
    
    // 稀疏数组
    // [1, , 3, 4, , 6].with(0, 2) // [2, undefined, 3, 4, undefined, 6]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      unrelwithed: 'foo',
      0: 'a',
      2: 'b',
    }
    Array.prototype.with.call(arrayLike, 0, 1) // [1, undefined, 'b']
    

排序方法

  • Array.prototype.reverse()

    语法: arr.reverse()

    描述: 反转数组中元素的位置,改变了数组,并返回该数组的引用

    • 会改变原数组,返回对同一数组的引用。
    • 对于稀疏数组,空槽(empty 元素)将以空槽的形式被复制到它们各自的新索引中。
    • 对于类数组对象,会读取 thislength 属性。然后,它访问 0length / 2 之间的每个索引,并交换两端对应的两个索引,并在必要时,删除某些属性。
    let arr = ['a', 'b', 'c']
    let arrReverse = arr.reverse() 
    console.log(arrReverse) // ['c', 'b', 'a']
    console.log(arr) // ['c', 'b', 'a']
    
    // 稀疏数组
    [1, , 3, 4, , 6].reverse() // [6, empty, 4, 3, empty, 1]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      unrelreverseed: 'foo',
      2: 'b',
    }
    Array.prototype.reverse.call(arrayLike)
    // {0: 'b', length: 3, unrelreverseed: 'foo'}
    // 索引 2 被删除,因为原本的数据中索引 0 不存在
    
  • Array.prototype.toReversed()

    语法: arr.toReversed()

    描述: 反转原数组中元素的位置,并返回新数组

    • reverse() 方法对应的复制版本。
    • 不会改变原数组,会返回一个新的浅拷贝数组。
    • 对于稀疏数组,原数组的空槽(empty 元素),在新数组中被替换成 undefined
    let arr1 = ['a', 'b', 'c']
    let arr1ToReversed = arr1.toReversed()
    console.log(arr1ToReversed) // ['c', 'b', 'a']
    console.log(arr1) // ['a', 'b', 'c']
    
    let arr2 = [{ color: 'red' }, 'foo', 1]
    let arr2ToReversed = arr2.toReversed() // [{ color: 'red' }]
    arr2ToReversed[2].color = 'yellow'
    console.log(arr2ToReversed) // [1, 'foo', { color: 'yellow' }]
    console.log(arr2) // [{ color: 'red' }, 'foo', 1]
    
    // 稀疏数组
    [1, , 3, 4, , 6].toReversed() // [6, undefined, 4, 3, undefined, 1]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      unreltoReverseded: 'foo',
      2: 'b',
    }
    Array.prototype.toReversed.call(arrayLike)
    // ['b', undefined, undefined]
    // '0' 和 '1' 两个索引不存在,所以它们会变成 undefined
    
  • Array.prototype.sort()

    语法: arr.sort(compareFn)

    描述: 对原数组的元素进行排序,并返回对相同数组的引用。

    • 会改变原数组,返回对同一数组的引用。
    • 如果未提供 compareFn
      • 所有非 undefined 的数组元素都会被转换为字符串,并按照 UTF-16 码元顺序比较字符串进行排序

        在 UTF-16 中,Unicode 字符超出 \uFFFF 的范围会被编码为两个代理码元(surrogate code unit),码位的范围是 \uD800\uDFFF。每个码位的值都会被单独考虑进行比较。因此,由代理对 \uD855\uDE51 组成的字符将排在字符 \uFF3A 的前面。

      • 所有的 undefined 元素都会被排序到数组的末尾。

    • 如果提供了 compareFn
      • 所有非 undefined 的数组元素都会按照比较函数的返回值进行排序

        • compareFn(a, b) > 0,则 ab 后(如 [b, a]
        • compareFn(a, b) < 0,则 ab 前(如 [a, b]
        • compareFn(a, b) === 0,则保持 ab 原有顺序
      • 所有的 undefined 元素都会被排序到数组的末尾,并且不调用 compareFn

    • 对于稀疏数组,空槽(empty 元素)会被移动到数组的末尾,并始终排在所有 undefined 元素的后面。
    • 对于类数组对象,会读取 thislength 属性。然后收集在 0length - 1 范围内所有已存在的整数键属性,对它们进行排序,然后写回。如果范围内存在缺失的属性,则相应的尾随属性将被删除,好像不存在的属性被排序到末尾一样。
    let stringArr = ['Blue', 'Humpback', 'Beluga']
    let numberArr = [40, 1, 5, 200]
    let numericStringArr = ['80', '9', '700']
    let mixedNumericArr = ['80', '9', '700', 40, 1, 5, 200]
    
    function compareNumbers(a, b) {
      return a - b
    }
    
    stringArr.sort() // ['Beluga', 'Blue', 'Humpback']
    numberArr.sort() // [1, 200, 40, 5]
    numberArr.sort(compareNumbers) // [1, 5, 40, 200]
    numericStringArr.sort() // ['700', '80', '9']
    numericStringArr.sort(compareNumbers) // ['9', '80', '700']
    mixedNumericArr.sort() // [1, 200, 40, 5, '700', '80', '9']
    mixedNumericArr.sort(compareNumbers) // [1, 5, '9', 40, '80', 200, '700']
    
    // 稀疏数组
    ['a', 'c', , 'b'].sort() // ['a', 'b', 'c', empty]
    [, undefined, 'a', 'b'].sort() // ['a', 'b', undefined, empty]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      unrelated: 'foo',
      0: 5,
      2: 4,
    }
    Array.prototype.sort.call(arrayLike)
    // {0: 4, 1: 5, length: 3, unrelated: 'foo'}
    
  • Array.prototype.toSorted()

    语法: arr.toSorted(compareFn)

    描述: 对原数组的元素进行排序,并返回新数组。

    • sort() 方法的复制方法版本。
    • 不会改变原数组,会返回一个新的浅拷贝数组。
    • 对于稀疏数组,原数组的空槽(empty 元素),在新数组中被替换成 undefined
    let stringArr = ['Blue', 'Humpback', 'Beluga']
    let numberArr = [40, 1, 5, 200]
    let numericStringArr = ['80', '9', '700']
    let mixedNumericArr = ['80', '9', '700', 40, 1, 5, 200]
    
    function compareNumbers(a, b) {
      return a - b
    }
    
    stringArr.toSorted() // ['Beluga', 'Blue', 'Humpback']
    numberArr.toSorted() // [1, 200, 40, 5]
    numberArr.toSorted(compareNumbers) // [1, 5, 40, 200]
    numericStringArr.toSorted() // ['700', '80', '9']
    numericStringArr.toSorted(compareNumbers) // ['9', '80', '700']
    mixedNumericArr.toSorted() // [1, 200, 40, 5, '700', '80', '9']
    mixedNumericArr.toSorted(compareNumbers) // [1, 5, '9', 40, '80', 200, '700']
    
    // 稀疏数组
    ['a', 'c', , 'b'].toSorted() // ['a', 'b', 'c', undefined]
    [, undefined, 'a', 'b'].toSorted() // ['a', 'b', undefined, undefined]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      unrelated: 'foo',
      0: 5,
      2: 4,
    }
    Array.prototype.toSorted.call(arrayLike) // [4, 5, undefined]
    

迭代方法

  • Array.prototype.forEach()

    语法: arr.forEach(callbackFn, thisArg)

    描述: 按索引升序地为数组中的每个元素调用一次提供的 callbackFn 函数,forEach() 总是返回 undefined

    • 不会改变原数组,提供 callbackFn 的函数可以更改原数组。
    • 在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:
      • callbackFn 不会访问超出数组初始长度的任何元素。
      • 已经访问过的索引的更改不会导致 callbackFn 再次调用。
      • 如果 callbackFn 更改了数组中已经存在但尚未访问的元素,则传递给 callbackFn 的值将是在访问该元素时的值。已经被删除的元素不会被访问。
    • 除非抛出异常,否则没有办法停止或中断 forEach() 循环。
    • forEach() 期望 callbackFn 是一个同步函数,不会等待 Promise 兑现。
    • 对于稀疏数组,空槽(empty 元素)不会被调用。
    • 对于类数组对象,读取 thislength 属性,然后访问每个整数索引。

    参数:

    • callbackFn :数组中每个元素执行的函数。该函数接收以下参数:
      • element :数组中正在处理的当前元素。
      • index :数组中正在处理的当前元素的索引。
      • array :调用了 forEach() 的数组本身。
    • thisArg :执行 callbackFn 时用作 this 的值。使用箭头函数表达式来传入函数参数, thisArg 参数会被忽略,因为箭头函数在词法上绑定了 this 值。
    const arr = ['a', 'b', 'c']
    arr.forEach((element) => console.log(element))
    // a
    // b
    // c
    
    // 稀疏数组
    const sparseArr = [1, 3 /* empty */, , 7]
    let numCallbackRuns = 0
    sparseArr.forEach((element) => {
      console.log({ element })
      numCallbackRuns++
    })
    console.log('numCallbackRuns : ', numCallbackRuns)
    // {element: 1}
    // {element: 3}
    // {element: 7}
    // numCallbackRuns : 3
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      0: 2,
      1: 3,
      2: 4,
    }
    Array.prototype.forEach.call(arrayLike, (x) => console.log(x))
    // 2
    // 3
    // 4
    
  • Array.prototype.map()

    语法: arr.map(callbackFn, thisArg)

    描述: 创建一个新数组,数组元素由原数组中每个元素调用提供的回调函数 callbackFn 的返回值组成。

    • 不会改变原数组,提供 callbackFn 的函数可以更改原数组。
    • 在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:
      • callbackFn 不会访问超出数组初始长度的任何元素。
      • 已经访问过的索引的更改不会导致 callbackFn 再次调用。
      • 如果 callbackFn 更改了数组中已经存在但尚未访问的元素,则传递给 callbackFn 的值将是在访问该元素时的值。已经被删除的元素不会被访问。
    • 对于稀疏数组,空槽(empty 元素)的索引在返回的数组中仍然为空槽,并且回调函数不会对它们进行调用。
    • 对于类数组对象,读取 thislength 属性,然后访问每个整数索引。

    参数:

    • callbackFn :原数组中每个元素执行的函数,返回值作为一个元素被添加为新数组中。该函数接收以下参数:
      • element :数组中正在处理的当前元素。
      • index :数组中正在处理的当前元素的索引。
      • array :调用了 map() 的数组本身。
    • thisArg :执行 callbackFn 时用作 this 的值。
    let numbersArr1 = [1, 4, 9]
    let roots1 = numbersArr1.map((num) => Math.sqrt(num))
    console.log(numbersArr1) // [1, 4, 9]
    console.log(roots1) // [ 1, 2, 3]
    
    // 稀疏数组
    let sparseArr = [1, , 3].map((x, index) => {
      console.log(`Visit ${index}`)
      return x * 2
    })
    console.log(sparseArr)
    // Visit 0
    // Visit 2
    // [2, empty, 6]
    
    const numbersArr2 = [1, 2, 3, 4]
    const filteredNumbers = numbersArr2.map((num, index) => {
      if (index < 3) {
        return num
      }
    })
    console.log(numbersArr2) // [1, 2, 3, 4]
    console.log(filteredNumbers) // [1, 2, 3, undefined]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      0: 2,
      1: 3,
      2: 4,
    }
    Array.prototype.map.call(arrayLike, (x) => x ** 2) // [4, 9, 16]
    
  • Array.prototype.filter()

    语法: arr.filter(callbackFn, thisArg)

    描述: 用于过滤数组成员,返回满足条件的数组元素的浅拷贝。如果没有元素满足条件,则返回一个空数组。

    • 不会改变原数组,提供 callbackFn 的函数可以更改原数组。
    • 在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:
      • callbackFn 不会访问超出数组初始长度的任何元素。
      • 已经访问过的索引的更改不会导致 callbackFn 再次调用。
      • 如果 callbackFn 更改了数组中已经存在但尚未访问的元素,则传递给 callbackFn 的值将是在访问该元素时的值。已经被删除的元素不会被访问。
    • 对于稀疏数组,filter() 会跳过空槽(empty 元素)。
    • 对于类数组对象,读取 thislength 属性,然后访问每个整数索引。

    参数:

    • callbackFn :数组中的每个元素执行的函数。该函数应该返回一个 Truthy(真值)以将元素保留在结果数组中。该函数接收以下参数:
      • element :数组中正在处理的当前元素。
      • index :数组中正在处理的当前元素的索引。
      • array :调用了 filter() 的数组本身。
    • thisArg :执行 callbackFn 时用作 this 的值。
    let arr1 = [12, 5, 8, 130, 44]
    let filterArr1 = arr1.filter((element) => element >= 10)
    console.log(filterArr1) // [12, 130, 44]
    console.log(arr1) // [12, 5, 8, 130, 44]
    
    // 稀疏数组
    let sparseArr = [1, , undefined].filter((element) => element !== 2)
    console.log(sparseArr) // [1, undefined]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      0: 'a',
      1: 'b',
      2: 'c',
    }
    Array.prototype.filter.call(arrayLike, (x) => x <= 'b')
    // ['a', 'b']
    
  • Array.prototype.every()

    语法: arr.every(callbackFn, thisArg)

    描述: 测试一个数组内的所有元素是否都能通过指定函数 callbackFn 的测试,返回一个布尔值。

    • 不会改变原数组,提供 callbackFn 的函数可以更改原数组。
    • 在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:
      • callbackFn 不会访问超出数组初始长度的任何元素。
      • 已经访问过的索引的更改不会导致 callbackFn 再次调用。
      • 如果 callbackFn 更改了数组中已经存在但尚未访问的元素,则传递给 callbackFn 的值将是在访问该元素时的值。已经被删除的元素不会被访问。
    • 对于稀疏数组,空槽(empty 元素)不会执行 callbackFn
    • 对于类数组对象,读取 thislength 属性,然后访问每个整数索引,直到到达末尾或 callbackFn 返回 false

    参数:

    • callbackFn :数组中的每个元素执行的函数。该函数应该返回一个 Truthy(真值)表示元素通过测试。该函数接收以下参数:
      • element :数组中正在处理的当前元素。
      • index :数组中正在处理的当前元素的索引。
      • array :调用了 every() 的数组本身。
    • thisArg :执行 callbackFn 时用作 this 的值。
    const isSubset = (array1, array2) =>
      array2.every((element) => array1.includes(element))
    isSubset([1, 2, 3, 4, 5, 6, 7], [5, 7, 6]) // true
    
    let arr = [1, 2, 3, 4]
    arr.every((elem, index, arr) => {
      arr[index + 1]--
      console.log(`[${arr}][${index}] -> ${elem}`)
      return elem < 2
    })
    // 循环会迭代 3 次
    // 没有修改的情况下只会迭代 2 次
    // 第 1 次迭代:[1,1,3,4][0] -> 1
    // 第 2 次迭代:[1,1,2,4][1] -> 1
    // 第 3 次迭代:[1,1,2,3][2] -> 2
    
    // 稀疏数组
    [1, , 3].every((element) => element !== undefined) // true
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      0: 'a',
      1: 'b',
      2: 'c',
    }
    Array.prototype.every.call(arrayLike, (x) => typeof x === 'string') // true
    
  • Array.prototype.some()

    语法: arr.some(callbackFn, thisArg)

    描述: 测试数组中是否至少有一个元素通过了由提供的函数 callbackFn 实现的测试。如果在数组中找到一个元素,使得提供的函数返回 true,则返回 true;否则返回 false

    • 不会改变原数组,提供 callbackFn 的函数可以更改原数组。
    • some() 方法如果找到满足条件的元素,将会立即返回 true 并停止遍历数组。
    • 对于空数组,任何条件下它都返回 false
    • 在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:
      • callbackFn 不会访问超出数组初始长度的任何元素。
      • 已经访问过的索引的更改不会导致 callbackFn 再次调用。
      • 如果 callbackFn 更改了数组中已经存在但尚未访问的元素,则传递给 callbackFn 的值将是在访问该元素时的值。已经被删除的元素不会被访问。
    • 对于稀疏数组,空槽(empty 元素)不会执行 callbackFn
    • 对于类数组对象,读取 thislength 属性,然后访问每个整数索引,直到到达末尾或 callbackFn 返回 true

    参数:

    • callbackFn :数组中的每个元素执行的函数。该函数应该返回一个 Truthy(真值)表示元素通过测试。该函数接收以下参数:
      • element :数组中正在处理的当前元素。
      • index :数组中正在处理的当前元素的索引。
      • array :调用了 every() 的数组本身。
    • thisArg :执行 callbackFn 时用作 this 的值。
    [12, 5, 8, 1, 4].some((x) => x > 10) // true
    
    // 稀疏数组
    [1, undefined, 1].some((x) => x !== 1) // true
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      0: 'a',
      1: 'b',
      2: 'c',
    }
    Array.prototype.some.call(arrayLike, (x) => typeof x === 'number')
    // false
    

归并方法

  • Array.prototype.reduce() / Array.prototype.reduceRight()

    语法: arr.reduce(callbackFn, initialValue) / arr.reduceRight(callbackFn, initialValue)

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

    • 不会改变原数组,提供 callbackFn 的函数可以更改原数组。
    • 在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:
      • callbackFn 不会访问超出数组初始长度的任何元素。
      • 已经访问过的索引的更改不会导致 callbackFn 再次调用。
      • 如果 callbackFn 更改了数组中已经存在但尚未访问的元素,则传递给 callbackFn 的值将是在访问该元素时的值。已经被删除的元素不会被访问。
    • 对于稀疏数组,空槽(empty 元素)不会执行 callbackFn
    • 对于类数组对象,读取 thislength 属性,然后访问每个整数索引。
    • reduce()reduceRight() 之间的区别
      • reduce() 方法从左至右遍历
      • reduceRight() 方法从右至左遍历

    参数:

    • callbackFn :为数组中每个元素执行的函数。其返回值将作为下一次调用 callbackFn 时的 accumulator 参数。对于最后一次调用,返回值将作为 reduce() 的返回值。该函数接收以下参数:
    • accumulator :上一次调用 callbackFn 的结果。在第一次调用时,如果指定了 initialValue 则为指定的值,否则为 array[0] 的值。
      • currentValue :当前元素的值。在第一次调用时,如果指定了 initialValue,则为 array[0] 的值,否则为 array[1]
      • currentIndexcurrentValue 在数组中的索引位置。在第一次调用时,如果指定了 initialValue 则为 0,否则为 1
      • array :调用了 reduce() 的数组本身。
    • initialValue :第一次调用回调时初始化 accumulator 的值。
      • 如果指定了 initialValue
        • callbackFn数组中的第一个值(对于 reduceRight() 方法则为数组中的倒数第一个值) 作为 currentValue 开始执行。
      • 如果没有指定 initialValue
        • accumulator 初始化为数组中的第一个值(对于 reduceRight() 方法则为数组中的倒数第一个值)
        • callbackFn数组中的第二个值(对于 reduceRight() 方法则为数组中的倒数第二个值) 作为 currentValue 开始执行。
      • 如果数组只有一个元素(无论其位置如何)且没有提供 initialValue,或者提供了 initialValue 但数组为空,则直接返回该单个值,且 callbackFn 不会被调用。
      • 如果数组为空且没有提供 initialValue(没有第一个值可以作为 accumulator 返回),则会抛出 TypeError 异常。
    // 未提供 initialValue
    // 对于长度大于 1、等于 1 和 0 的数组,reduce 方法的不同表现
    
    const getMax = (a, b) => Math.max(a, b)
    
    // 从索引 0 开始为数组中的每个元素调用回调函数
    [1, 100].reduce(getMax, 50) // 100
    [50].reduce(getMax, 10) // 50
    
    // 仅为索引 1 处的元素调用回调函数
    [1, 100].reduce(getMax) // 100
    
    // 不调用回调函数
    [50].reduce(getMax) // 50
    [].reduce(getMax, 1) // 1
    
    [].reduce(getMax) // TypeError
    
    // ==============================
    
    const array = [15, 16, 17, 18]
    
    function reducer(accumulator, currentValue, index) {
      const returns = accumulator + currentValue
      console.log(
        `accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`
      )
      return returns
    }
    
    array.reduce(reducer) // 66
    
    // accumulator: 15, currentValue: 16, index: 1, returns: 31 
    // accumulator: 31, currentValue: 17, index: 2, returns: 48
    // accumulator: 48, currentValue: 18, index: 3, returns: 66
    
    // ==============================
    
    // reduce 与 reduceRight 之间的区别
    const arr = ['1', '2', '3', '4', '5']
    const left = arr.reduce((prev, cur) => prev + cur)
    const right = arr.reduceRight((prev, cur) => prev + cur)
    
    console.log(left) // '12345'
    console.log(right) // '54321'
    
    // ==============================
    
    // 数组去重
    const myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd']
    const myArrayWithNoDuplicates = myArray.reduce((accumulator, currentValue) => {
      if (!accumulator.includes(currentValue)) {
        return [...accumulator, currentValue]
      }
      return accumulator
    }, [])
    console.log(myArrayWithNoDuplicates) // ['a', 'b', 'c', 'e', 'd']
    
    // 展平嵌套数组
    const flattened = [
      [0, 1],
      [2, 3],
      [4, 5],
    ].reduce((accumulator, currentValue) => accumulator.concat(currentValue), [])
    // [0, 1, 2, 3, 4, 5]
    
    // ==============================
    
    // 稀疏数组:会跳过空槽(emtpy 元素),不会跳过 undefined 值
    [1, 2, undefined, 4].reduce((a, b) => a + b) // NaN
    
    // ==============================
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      0: 2,
      1: 3,
      2: 4,
    }
    Array.prototype.reduce.call(arrayLike, (x, y) => x + y) // 9
    

数组扁平化

  • Array.prototype.flat()

    语法: arr.flat(depth)

    描述: 创建一个新的数组,并根据指定深度 depth 递归地将所有子数组元素拼接到新的数组中。

    • 不会改变原数组,会返回一个新的浅拷贝数组。
    • 如果待展开的数组是稀疏的,flat() 方法会忽略其中的空槽(empty 元素)。例如,如果 depth1,那么根数组和第一层嵌套数组中的空槽都会被忽略,但在更深的嵌套数组中的空槽则会与这些数组一起保留。
    • 对于类数组对象,读取 thislength 属性,然后访问每个整数索引。如果元素不是数组,则直接将其附加到结果中。如果元素是数组,则根据 depth 参数进行展开操作。
    [1, 2, [3, 4]].flat() // [1, 2, 3, 4]
    [1, 2, [3, 4, [5, 6]]].flat() // [1, 2, 3, 4, [5, 6]]
    [1, 2, [3, 4, [5, 6]]].flat(2) // [1, 2, 3, 4, 5, 6]
    [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]].flat(Infinity) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    // 稀疏数组
    [1, 2, , 4, 5].flat() // [1, 2, 4, 5]
    [1, , 3, ['a', , 'c']].flat() // [ 1, 3, 'a', 'c']
    [1, , 3, ['a', , ['d', , 'e']]].flat() // [1, 3, 'a', ['d', empty, 'e']]
    [1, , 3, ['a', , ['d', , 'e']]].flat(2) // [1, 3, 'a', 'd', 'e']
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      0: [1, 2],
      // 嵌套的类数组对象不会被展平
      1: { length: 2, 0: 3, 1: 4 },
      2: 5,
    }
    Array.prototype.flat.call(arrayLike) // [1, 2, { 0: 3, 1: 4, length: 2 }, 5]
    
  • Array.prototype.flatMap()

    语法: arr.flatMap(callbackFn, thisArg)

    描述: 对数组中的每个元素应用给定的回调函数 callbackFn,然后将结果展开一级,返回一个新数组。

    • 等价于在调用 map() 方法后再调用深度为 1flat() 方法(arr.map(...args).flat()),但比分别调用这两个方法稍微更高效一些。
    • 对于稀疏数组,空槽(empty 元素)不会执行 callbackFn,对于 callbackFn 返回的空槽,flatMap 将忽略返回数组中的空槽。
    • 对于类数组对象,会读取 thislength 属性,然后访问每个整数索引。如果回调函数的返回值不是数组,则始终直接将其附加到结果数组的末尾。

    参数:

    • callbackFn :一个在数组的每个元素上执行的函数。函数应该返回一个包含新数组元素的数组,或是要添加到新数组中的单个非数组值。该函数接收以下参数:
      • element :数组中正在处理的当前元素。
      • index :数组中正在处理的当前元素的索引。
      • array :调用 flatMap() 的当前数组。
    • thisArg :可选值。在执行 callbackFn 时用作 this 的值。
    const arr1 = ["it's Sunny in", '', 'California']
    arr1.map((x) => x.split(' ')) // [["it's", 'Sunny', 'in'], [''],['California']]
    arr1.flatMap((x) => x.split(' ')) // ["it's", 'Sunny', 'in', '', 'California']
    
    // 稀疏数组
    [1, 2, , 4, 5].flatMap((x) => [x, x * 2]) // [1, 2, 2, 4, 4, 8, 5, 10]
    [1, 2, 3, 4].flatMap((x) => [, x * 2]) // [2, 4, 6, 8]
    
    // 类数组对象
    const arrayLike = {
      length: 3,
      0: 1,
      1: 2,
      2: 3,
    }
    Array.prototype.flatMap.call(arrayLike, (x) => [x, x * 2])
    // [1, 2, 2, 4, 3, 6]
    Array.prototype.flatMap.call(arrayLike, (x) => ({
      length: 1,
      0: x,
    }))
    // [{0: 1, length: 1}, {0: 2, length: 1}, {0: 3, length: 1}]
    
上次编辑于:
贡献者: lingronghai,lrh21g