JavaScript中数组方法总结

2021/6/21 JavaScript

# Array属性

  • Array.length

    Array构造函数的length属性,其值为1(注意该属性为静态属性,不是数组实例的length属性)。

  • get Array[@@species]

    返回Array构造函数,Array[Symbol.species]; // function Array()

  • Array.prototype

    通过数组的原型对象可以为所有数组对象添加属性,鲜为人知的事实:Array.prototype本身也是一个Array

# Array方法

  • Array.from()

    从类数组对象或者可迭代对象中创建一个新的数组实例。

    Array.from(arrayLike[, mapFn[, thisArg]])

  • Array.isArray()

    用来判断某个变量是否是一个数组对象。

    Array.isArray(Array.prototype); // true

  • Array.of()

    根据一组参数来创建新的数组实例,支持任意的参数数量和类型。Array.of()Array构造函数之间的区别在于处理整数参数:Array.of(7)创建一个具有单个元素7的数组,而Array(7)创建一个长度为7的空数组(注意:这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。

# 数组实例属性

  • Array.prototype.constructor

    所有的数组实例都继承了这个属性,它的值就是Array,表明了所有的数组都是由Array构造出来的。

  • Array.prototype.length

    上面说了,因为Array.prototype也是个数组,所以它也有length属性,这个值为0,因为它是个空数组(但是有其他属性)。

# 数组实例方法

# 修改器方法

WARNING

下面的这些方法会改变调用它们的对象自身的值

  • Array.prototype.copyWithin()

    • 描述:浅复制数组的一部分到同一数组中的另一个位置,覆盖原有的值,并返回它,不会改变原数组的长度。
    • 语法:arr.copyWithin(target[, start[, end]])
    • 返回值:改变后的数组。
  • Array.prototype.fill()

    • 描述:用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引
    • 语法:arr.fill(value[, start[, end]])
    • 返回值:修改后的数组。
  • Array.prototype.shift()

    • 描述:从数组中删除第一个元素,并返回该元素的值。
    • 语法:arr.shift()
    • 返回值:从数组中删除的元素,如果数组为空则返回undefined
  • Array.prototype.unshift()

    • 描述:将一个或多个元素添加到数组的开头,并返回该数组的新长度。
    • 语法:arr.unshift(element1, ..., elementN)
    • 返回值:新的length属性值。
  • Array.prototype.pop()

    • 描述:从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
    • 语法:arr.pop()
    • 返回值:从数组中删除的元素,如果数组为空则返回undefined
  • Array.prototype.push()

    • 描述:将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
    • 语法:arr.push(element1, ..., elementN)
    • 返回值:新的length属性值。
  • Array.prototype.reverse()

    • 描述:将数组中元素的位置颠倒,并返回该数组。
    • 语法:arr.reverse()
    • 返回值:颠倒后的数组。
  • Array.prototype.sort()

    • 描述:用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后按照它们的UTF-16码元值升序排序。
    • 语法:arr.sort([compareFunction])
    • 返回值:排序后的数组。
  • Array.prototype.splice()

    • 描述:通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。
    • 语法:array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
    • 返回值:一个包含了删除的元素的数组。

# 访问方法

WARNING

下面的这些方法绝对不会改变调用它们的对象的值,只会返回一个新的数组或者返回一个其它的期望值。

  • Array.prototype.concat()

    • 描述:用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
    • 语法:var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])
    • 返回值:新的Array实例。
  • Array.prototype.includes()

    • 描述:用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false
    • 语法:arr.includes(valueToFind[, fromIndex])
    • 返回值:返回一个布尔值,如果在数组中找到了(如果传入了fromIndex,表示在fromIndex指定的索引范围中找到了)则返回true
  • Array.prototype.join()

    • 描述:将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
    • 语法:arr.join([separator])
    • 返回值:一个所有数组元素连接的字符串。如果arr.length为0,则返回空字符串。如果一个元素为undefinednull,它会被转换为空字符串。
  • Array.prototype.slice()

    • 描述:返回一个新的数组对象,这一对象是一个由beginend决定的原数组的浅拷贝(包括begin,不包括end)。
    • 语法:arr.slice([begin[, end]])
    • 返回值:一个含有被提取元素的新数组。
  • Array.prototype.toString()

    • 描述:返回一个字符串,表示指定的数组及其元素。
    • 语法:arr.toString()
    • 返回值:一个表示指定的数组及其元素的字符串,其中包含用逗号分隔的每个数组元素。如果join方法不可用或者不是函数,则会使用Object.prototype.toString来代替,并返回[object Array]
  • Array.prototype.toLocaleString()

    • 描述:返回一个字符串表示数组中的元素。数组中的元素将使用各自的toLocaleString方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号“,”)隔开。
    • 语法:arr.toLocaleString([locales[,options]]);
    • 返回值:表示数组元素的字符串。
  • Array.prototype.indexOf()

    • 描述:返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
    • 语法:arr.indexOf(searchElement[, fromIndex])
    • 返回值:首个被找到的元素在数组中的索引位置,若没有找到则返回-1。
  • Array.prototype.lastIndexOf()

    • 描述:返回指定元素在数组中的最后一个的索引,如果不存在则返回-1。从数组的后面向前查找,从fromIndex处开始。
    • 语法:arr.lastIndexOf(searchElement[, fromIndex])
    • 返回值:数组中该元素最后一次出现的索引,如未找到返回-1。
  • Array.prototype.toSource()非标准

    • 描述:返回一个字符串,代表该数组的源代码。
    • 语法:array.toSource()
    • 返回值:返回一个字符串。

TIPS

有索引参数方法总结

  • copyWithin
  • fill
  • splice
  • includes
  • slice
  • indexOf
  • lastIndexOf
  1. 如果索引值index大于0,没有任何特殊含义,就是指定匹配位置,如果是超出数组长度则表示能匹配到数组的结尾。
  2. 如果索引值index小于0,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于array.length - n)。如果负数的绝对值大于数组的长度,则表示能匹配到数组开始的0位。
  3. 如果传入的索引是一个区间,startend,那么能取到的值都是左闭右开的:[start, end),即都是取不到end位处的元素,例如copyWithinfillslicesplice不同的是传入的是开始索引和截取元素的个数。

# 迭代方法

WARNING

在每一个数组元素都分别执行完回调函数之前,数组的length属性会被缓存在某个地方,所以,如果你在回调函数中为当前数组添加了新的元素,那么那些新添加的元素是不会被遍历到的。此外,如果在回调函数中对当前数组进行了其它修改,比如改变某个元素的值或者删掉某个元素,那么随后的遍历操作可能会受到未预期的影响。

  • Array.prototype.forEach()

    • 描述:对数组的每个元素执行一次给定的函数。
    • 语法:arr.forEach(callback(element[, index[, array]])[, thisArg])
    • 返回值:undefined
  • Array.prototype.map()

    • 描述:创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
    • 语法:var newArray = arr.map(callback(element[, index[, array]]){ return element }[, thisArg])
    • 返回值:一个由原数组每个元素执行回调函数的结果组成的新数组。如果被map调用的数组是离散的,新数组将也是离散的保持相同的索引为空。
  • Array.prototype.every()

    • 描述:测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。若收到一个空数组,此方法在一切情况下都会返回 true。
    • 语法:arr.every(callback(element[, index[, array]])[, thisArg])
    • 返回值:如果回调函数的每一次返回都为truthy值,返回true,否则返回false
  • Array.prototype.some()

    • 描述:测试数组中是不是至少有1个元素通过了被提供的函数测试。如果用一个空数组进行测试,在任何情况下它返回的都是false
    • 语法:arr.some(callback(element[, index[, array]])[, thisArg])
    • 返回值:数组中有至少一个元素通过回调函数的测试就会返回true;所有元素都没有通过回调函数的测试返回值才会为false
  • Array.prototype.filter()

    • 描述:创建一个新数组,其包含通过所提供函数实现的测试的所有元素。
    • 语法:var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
    • 返回值:一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。
  • Array.prototype.find()

    • 描述:返回数组中满足提供的测试函数的第一个元素的值。否则返回undefined
    • 语法:arr.find(callback(element[, index[, array]])[, thisArg])
    • 返回值:数组中第一个满足所提供测试函数的元素的值,否则返回undefined
  • Array.prototype.findIndex()

    • 描述:返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。
    • 语法:arr.findIndex(callback(element[, index[, array]])[, thisArg])
    • 返回值:数组中通过提供测试函数的第一个元素的索引。否则,返回-1
  • Array.prototype.reduce()

    • 描述:对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
    • 语法:arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
    • 返回值:函数累计处理的结果。
  • Array.prototype.reduceRight()

    • 描述:接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
    • 语法:arr.reduceRight(callback(accumulator, currentValue[, index[, array]])[, initialValue])
    • 返回值:函数累计处理的结果。
  • Array.prototype.entries()

    • 描述:返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对。
    • 语法:arr.entries()
    • 返回值:一个新的Array迭代器对象。
  • Array.prototype.keys()

    • 描述:返回一个包含数组中每个索引键的Array Iterator对象。索引迭代器会包含那些没有对应元素的索引。
    • 语法:arr.keys()
    • 返回值:一个新的Array迭代器对象。
  • Array.prototype.values()

    • 描述:返回一个新的Array Iterator对象,该对象包含数组每个索引的值。
    • 语法:arr.values()
    • 返回值:一个新的Array迭代对象。
    • Array.prototype.valuesArray.prototype[Symbol.iterator]的默认实现。
    • Array.prototype.values === Array.prototype[Symbol.iterator] // true
  • Array.prototype[@@iterator]()

    • 描述:@@iterator属性和Array.prototype.values()属性的初始值是同一个函数对象。
    • 语法:arr[Symbol.iterator]()
    • 返回值:数组的iterator方法,默认情况下,与values()返回值相同,arr[Symbol.iterator]则会返回values()函数。

# 迭代方法小结

  1. forEacheverysomefilterfindfindIndexmap接收的callback参数都是一样callback(element[, index[, array]],并且第二个参数都是执行回调时用作this的对象thisArgreducereduceRight参数是一样的,一个reducer函数和一个初始值。

  2. thisArg参数(this),每次调用时,它都被传给callback函数,作为它的this值。注意:如果使用箭头函数表达式来传入函数参数,thisArg参数会被忽略,因为箭头函数在词法上绑定了this值。

  3. 所有的迭代方法都不会直接改变调用它的对象,但是那个对象可能会被callback函数改变。

    EMCA语言规范:'forEach does not directly mutate the object on which it is called but the object may be mutated by the calls to callbackfn.'

  4. 对于findfindIndexcallback函数会为数组中的每个索引调用即从0到length - 1,而不仅仅是那些被赋值的索引,这意味着对于稀疏数组来说,该方法的效率要低于那些只遍历有值的索引的方法。除开这两个之外的迭代方法在执行callback时都会跳过未被赋值的索引(直接修改索引操作造成的empty值或者使用delete删除的值)。对于map,虽然会跳过未被赋值的索引,但是最后得到的结果数组依旧保持了原有的未被赋值的索引。

# 其他

  • Array.prototype.at()

    • 描述:接受整数值并返回该索引处的项,允许正整数和负整数。负整数从数组中的最后一项开始倒数。
    • 语法:arr.at(index)
    • 返回值:该索引处的项。[1, 2, 3].at(-1); // 3
  • Array.prototype.flat()

    • 描述:按照一个可指定的深度(默认值为1)递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
    • 语法:var newArray = arr.flat([depth])
    • 返回值:一个包含将数组与子数组中所有元素的新数组。
  • Array.prototype.flatMap()

    • 描述:flatMap()方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。flatMap方法与map方法和深度depth为1的flat几乎相同,但flatMap通常在合并成一种方法的效率稍微高一些。
    • 语法:var new_array = arr.flatMap(function callback(currentValue[, index[, array]]) { // return element for new_array}[, thisArg])
    • 返回值:一个新的数组,其中每个元素都是回调函数的结果,并且结构深度depth值为1。

# 手动实现部分方法

# forEach

if (!Array.prototype.forEach) {
  Array.prototype.forEach = function (callback, thisArg) {
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    if (typeof callback !== 'function') {
      throw new TypeError(`${callback} is not a function`);
    }
    // 抽象操作ToObject将其参数转换为Object类型的值
    // 个人目前发现是可以防止其他非对象值在后面使用in时报错
    // 但是不清楚真正目的
    const O = Object(this);

    // O.length >>> 0 无符号右移 0 位,保证转换后的值为正整数。其实底层做了 2 层转换,
    // 第一是非 number 转成 number 类型,第二是将 number 转成 Uint32 类型。
    const length = O.length >>> 0;

    let k = 0;
    while (k < length) {
      // 使用 in 来判断索引 确保能够跳过那些empty的位
      if (k in O) {
        // thisArg 没传就是 undefined
        callback.call(thisArg, O[k], k, O);
      }
      k++;
    }
    // return undefined
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# map

if (!Array.prototype.map) {
  Array.prototype.map = function (callback, thisArg) {
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    if (typeof callback !== 'function') {
      throw new TypeError(`${callback} is not a function`);
    }
    // 抽象操作ToObject将其参数转换为Object类型的值
    const O = Object(this);

    // O.length >>> 0 无符号右移 0 位,保证转换后的值为正整数。其实底层做了 2 层转换,
    // 第一是非 number 转成 number 类型,第二是将 number 转成 Uint32 类型。
    const length = O.length >>> 0;

    let k = 0;
    // 这样保证了原来是empty的位,map后还是empty
    const mapResult = new Array(length);

    while (k < length) {
      if (k in O) {
        mapResult[k] = callback.call(thisArg, O[k], k, O);
      }
      k++;
    }
    return mapResult;
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# reduce

// redecuRight 原理差不多只是遍历索引从右往左
if (!Array.prototype.reduce) {
  Array.prototype.reduce = function (callback, initialValue) {
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    if (typeof callback !== 'function') {
      throw new TypeError(`${callback} is not a function`);
    }
    // 抽象操作ToObject将其参数转换为Object类型的值
    const O = Object(this);

    // O.length >>> 0 无符号右移 0 位,保证转换后的值为正整数。其实底层做了 2 层转换,
    // 第一是非 number 转成 number 类型,第二是将 number 转成 Uint32 类型。
    const length = O.length >>> 0;

    let k = 0;

    let value;
    if (arguments.length >= 2) {
      // 传了初始值
      value = initialValue;
    } else {
      // 找到一个非empty的有效值
      while (k < length && !(k in O)) {
        k++;
      }
      if (k >= length) {
        throw new TypeError('Reduce of empty array with no initial value');
      }
      value = O[k];
      // 用了数组中的一个初始值 那么累加的从下一个开始
      k++;
    }

    while (k < length) {
      if (k in O) {
        value = callback(value, O[k], k, O);
      }
      k++;
    }
    return value;
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# flat

if (!Array.prototype.flat) {
  Array.prototype.flat = function (depth = 1) {
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    const result = []; // 缓存递归结果
    // 开始递归
    (function flat(_arr, _depth) {
      // forEach 遍历数组会自动跳过空元素
      _arr.forEach((item) => {
        // 控制递归深度
        if (Array.isArray(item) && _depth > 0) {
          // 递归数组
          flat(item, _depth - 1);
        } else {
          // 缓存元素
          result.push(item);
        }
      });
    }(this, depth));
    // 返回递归结果
    return result;
  };
}

// function flatten(arr) {
//   while (arr.some((item) => Array.isArray(item))) {
//     arr = [].concat(...arr);
//     // concat接收的值可以是数组/值,如果是数据类型如字符串,数字和布尔
//     // concat将字符串和数字的值复制到新数组中
//   }
//   return arr;
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
最近更新: 2024年08月14日 16:42:18