1. 特点

ECMAScript 数组每一项都可以保存任何类型的数据,数组的大小是可以动态的调整的,可以随着数据的添加自动增长以容纳新数据

  1. 以数字作为索引(属性名),索引从零开始递增
  2. 有一个 length 属性存储数组长度
  3. 数组是 Array 类型的对象

按照四个维度记忆:

  • 方法的作用
  • 方法的参数
  • 方法的返回值
  • 原有数组是否改变

2. 创建方法

使用 **Array** 构造函数

  1. var colors = new Array(); // 创建空数组
  2. var colors = new Array(3); // 传递数值,创建长度为 3 的数组
  3. var colors = new Array("red", "blue", "green"); // 传递非数值,创建包含这些参数的数组

数组字面量表示法

  1. var colors = ["red", "blue", "green"];
  2. var colors = [];

不会调用 Array 构造函数

出现的问题:

  1. var values = [1, 2, ];
  2. var options = [ , , , , , ];

在 IE 中,values 会成为一个包含 3 个项且每一个项的值分别为 1、2、undefined 的数组,而在其他浏览器中,values 会成为包含 2 个项且值为 1、 2 的数组。

options 在 IE 9+ 和其他浏览器中,会是 5 个项的数组,不会计算最后一个逗号后面的空值,在 IE 8及以下,会包含 6 个项。

3. length 属性

数组的项数保存在其 length 属性中,这个属性始终返回 0 或更大的值。

数组的 length 属性不是只读的,可以通过这个属性,从数组的末尾移除项或向数组中添加新项。

  1. var colors = ["red", "blue", "green"];
  2. colors.length = 2;
  3. alert(colors[2]); // undefined

将数组设置为 2 就移除最后一项,最后一项的值就变成了 undefined

如果设置的 length 属性大于数组项数的值,则新增的每一项都会取得 undefined

利用 length 属性的动态改变,也可以方便地在数组末尾添加新项

  1. var colors = ["red", "blue", "green"];
  2. colors[colors.length] = "black"; // 末尾添加一个元素
  3. colors[colors.length] = "brown"; // 末尾再添加一个元素

当把一个值放在超出当前数组大小的位置上时,数组会重新计算其 length 属性,其值等于最后一项的索引加 1

数组最多包含 4 294 967 295 项,超出会发生异常,创建初始大小接近上限的数组,可能会导致运行时间超长的脚本错误。

4. 数组的检测

4.1 instanceof 操作符

对于一个网页或者一个全局作用域而言,使用 instanceof 操作符

  1. if (value instanceof Array) {
  2. ...
  3. }

instanceof 操作符的问题在于,它假定只有一个全局执行环境。如果网页中有多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的 Array 构造函数。从一个框架出传入另一个框架的数组,与第二个框架原生的数组具有不同的构造函数。

4.2 Array.isArray 是否是数组

Array.isArray() 方法,确定某个值是不是数组,不管是在哪个全局执行环境创建的

  1. if (Array.isArray(value)) {
  2. ...
  3. }

5. 数组转换字符串

5.1 三种转换

toLocaleString()toString()valueOf()

  • toString() 方法会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串,会调用每一项的 toString() 方法

  • valueOf() 方法返回的是数组中的值,与 toString() 方法结果相同

  • toLocaleString() 方法与前面两个返回的值一样,但是调用的是每一项的 toLocaleString() 方法

注意:当数组中包含对象时,该项返回的是 [object Object],而不会显示这个对象

5.2 join 连接

  • 作用:把数组转换为字符串,可以设置每一项之间的连接符
  • 参数:指定的连接符,不传入参数或传入 undefined,则默认使用逗号隔开
  • 返回:转换的字符串
  • 原有数组不变

应用
基于 join 实现数组中每一项求和的功能

  1. var arr = [12,23,34];
  2. arr.join('+'); //=> '12+23+34'
  3. eval(arr.join('+')); //=> 69

6. 数组的增删改

6.1 push 后增加

  • 作用:向数组的末尾追加新的内容
  • 参数:追加内容,接受任意数量的参数,都追加到数组中
  • 返回值:新增后数组的长度
  • 原有数组改变
  1. var arr = [12, 23];
  2. arr.push(34); //=> 3
  3. arr //=> [12,23,34]
  4. arr.push(45, 56); //=> 5
  5. arr //=> [12,23,34,45,56]

push()pop() 属于栈方法

6.2 pop 后删除

  • 作用:删除数组的最后一项
  • 参数:无
  • 返回:被删除的那一项内容
  • 原有数组改变
  1. var arr = [12, 23, 34];
  2. arr.pop(); //=> 34
  3. arr //=> [12,23]

6.3 shift 前删除

  • 作用:删除数组的第一项
  • 参数:无
  • 返回:被删除的那一项内容
  • 原有数组改变
  1. var arr = [12, 23, 34];
  2. arr.shift(); //=> 12
  3. arr //=> [23,34]

基于 shift 方法删除数组中的第一项,第一项被删除后,原有后面的每一项的索引都要向前减 1(往前提一位),称为数组塌陷

push() 方法组合来模拟队列

6.4 unshift 前增加

  • 作用:向数组开始位置追加内容
  • 参数:追加内容,任意个项
  • 返回值:新增后数组长度
  • 原有数组改变
  1. var arr = [12, 23, 34];
  2. arr.unshift(10, 11); //=> 5
  3. arr //=> [10,11,12,23,34]

pop() 方法结合可以反向模拟队列

6.5 splice 增加、删除、替换

作用:删除指定位置内容、向数组指定位置插入内容、修改指定位置信息

  1. splice(index, howmany, [item1, item2,...]);

删除

  • arr.splice(index, howmany)
  • 指定两个参数,从 index 索引开始,删除 howmany 项,index 项也删除
  • 如果不指定 howmany 或者指定的值大于当前数组,都是删除至数组末尾
  • 返回:删除的部分组成的新数组
  • 原有数组改变
  1. var arr = [12,23,34,45,56,67];
  2. arr.splice(2,3); //=> [34,45,56]
  3. arr //=> [12,23,67]
  4. arr.splice(1); //=> [23,67]
  5. arr //=> [12]

注意:基于 splice 的删除,会造成数组塌陷的问题,也就是后面项的索引会向前补全,原先项的索引改变


插入

  • splice(index, 0, [item1, item2,...])
  • 指定三个以上参数,从索引 index 开始,删除 0 项,把后面的参数插入到数组中,索引 index 的前面
  • 返回:被删除的部分,也就是空数组
  • 原有数组改变
  1. var arr = [12,23,34,45,56,67];
  2. arr.splice(2,0,'aa','bb'); //=> []
  3. arr //=> [12,23,'aa','bb',34,45,56,67]

替换

  • splice(index, howmany, [item1, item2,...])
  • 指定三个以上参数,从索引 index 开始,删除 howmany 项,然后用后面的参数替换这部分
  • 返回:删除部分组成的新数组
  • 原有数组改变
  1. var arr = [12,23,34,45,56,67];
  2. arr.splice(2,3,'aa','bb'); //=> [34, 45, 56]
  3. arr //=> [12,23,'aa','bb',67]

7. 数组的克隆

7.1 slice 抽取

  • 作用:在一个数组中,按照条件查找出数组中的部分内容,基于这些内容创建一个新数组。
  • 参数:两个参数,起始位置和结束位置
    • 没有参数,则返回整个数组克隆出来的新数组
    • 一个参数,返回从该参数指定位置开始到当前数组末尾的所有项
    • 两个参数,返回起始和结束位置之间的项,不包括结束位置的项
  • 返回:查找到的内容创建的新数组
  • 原有数组不变
  1. slice([start], [end]);

注意:第一个参数要比第二个小,否则返回空数组

实现数组的克隆

克隆一个新数组,和原有数组内容一样,但是存储的堆内存空间不同,是不相等且相互独立的

  1. //=> 两种方式作用一样
  2. arr.slice(0)
  3. arr.slice()

支持负数索引

  • 参数中有负数,则是从后面开始算起
  • 负数运算规则:数组总长度 + 负数索引
  • 如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从 0 开始。

7.2 concat 拼接

  • 作用:实现多个数组或者值的拼接
  • 参数:数组或者值
    • 没有参数,则返回整个数组克隆出来的新数组
    • 参数中不是数值的,简单地添加到结果数组的末尾
    • 参数中的数组,会将数组的每一项都添加到结果数组中
  • 返回:拼接后的新数组
  • 原有数组不变

实现步骤:
先创建当前数组的一个副本,然后将接受到的参数添加到副本的末尾,最后返回新构建的数组。

  1. var colors = ["red", "green", "blue"];
  2. colors.concat("yellow", ["black", "brown"]); //=> red, green, blue, yellow, black, brown

实现数组的克隆

克隆一个新数组,和原有数组内容一样,但是存储的堆内存空间不同,是不相等且相互独立的

  1. arr.concat();

拼接顺序

新数组的顺序为:参数按传入顺序添加、原数组

  1. //=> 基于空数组作为拼接的开始,在参数中排列顺序即可,空数组不会占据位置
  2. [].concat(arr1,arr2,arr3)

8. 数组的重排序

8.1 reverse 反转

  • 作用:反转数组项的顺序
  • 参数:无
  • 返回:排序后的数组
  • 原有数组改变
  1. var arr = [12,23,34];
  2. arr.reverse(); //=> [34,23,12]

8.2 sort 自定义排序

  • 作用:给数组排序
  • 参数:回调函数,指定排序的方法
  • 返回:排序后的数组
  • 原有数组改变

sort 不传递参数的排序

会调用数组每一项的 toString() 方法,得到字符串,然后基于字符串排序。

  1. //=> 基于 sort 本身的排序方法,数字不会按照我们预想的排序
  2. var arr = [1,19,2,24];
  3. arr.sort(); //=>[1, 19, 2, 24]

sort 方法可以接受一个比较函数,以便我们指定排序方法。
比较函数接受两个参数:

  • 如果第一个参数应该位于第二个之前则返回一个负数,交换位置
  • 如果两个参数相等则返回 0
  • 如果第一个参数应该位于第二个之后则返回一个正数,不交换位置

:::danger 注意:第一个参数是后面的数字,第二个参数是前面的数字
对于 1, 2, 3
a/b 分别为 2/1,3/2 :::

数字的排序

  1. function compare(value1, value2) {
  2. // 升序排列,降序为 return value2 - value1
  3. return value1 - value2;
  4. }
  5. var value = [5, 10, 1, 15];
  6. value.sort(compare);
  7. alert(value); // 1, 5, 10, 15

9. 数组的查找

这两种方法不兼容 IE 低版本浏览器(IE6-8)

9.1 indexOf 往后查找

  • 作用:往后查找当前值在数组中第一次出现的位置索引
  • 参数:要查找的值和开始查找的索引,默认从索引 0 开始查找
  • 返回:索引,没有找到返回 -1
  • 原有数组不变
  1. indexOf(searchValue, [fromIndex]);

在比较第一个参数和数组中的每一项时,使用的是全等操作符,必须严格相等

验证是否包含某一项

  1. if(arr.indexOf(value) > -1) {
  2. //=> 包含 value 时执行
  3. }

9.2 lastIndexOf 往前查找

  • 作用:往前查找当前值在数组中第一次出现的位置索引
  • 参数:要查找的值和开始查找的索引,默认从索引 0 开始查找
  • 返回:索引,没有找到返回 -1
  • 原有数组不变
  1. lastIndexOf(searchValue, [fromIndex]);

在比较第一个参数和数组中的每一项时,使用的是全等操作符,必须严格相等

注意:提供第二个参数时,lastIndexOf 会从索引位置开始往前查找

ES6 includes 包含

  • 作用:某个数组是否包含给定的值
  • 参数:需要判断的值、搜索的起始位置,第二个参数默认为 0。
  • 返回:包含则返回 true,不包含返回 false
  • 原有数组不变
  1. [1, 2, 3].includes(2) // true
  2. [1, 2, 3].includes(4) // false
  3. [1, 2, NaN].includes(NaN) // true

支持负数索引

  • 参数中有负数,则是从后面开始算起
  • 负数运算规则:数组总长度 + 负数索引
  • 如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从 0 开始。

支持 NaN 判断

indexOf 方法内部使用严格相等运算符(===)进行判断,这会导致对 NaN 的误判。

  1. [NaN].indexOf(NaN) //=> -1

includes 使用的是不一样的判断算法,就没有这个问题。

  1. [NaN].includes(NaN) //=> true

简易替代方案

检查当前环境是否支持该方法,如果不支持,部署一个简易的替代版本。

  1. const contains = (() =>
  2. Array.prototype.includes
  3. ? (arr, value) => arr.includes(value)
  4. : (arr, value) => arr.some(el => el === value)
  5. )();
  6. contains(['foo', 'bar'], 'baz'); //=> false

ES6 find() 和 findIndex()

  • 作用:找到第一个符合条件的项
  • 参数:第一个是一个回调函数,第二个是回调函数内的 this
  • 返回:符合条件的第一项的值或者索引,没有符合条件的返回 undefined 或者 -1
  • 原有数组不变

回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为 true 的成员,然后返回该成员。如果没有符合条件的成员,则返回 undefined

回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。

  1. [1, 4, -5, 10].find((n) => n < 0)
  2. // -5
  1. [1, 5, 10, 15].find(function(value, index, arr) {
  2. return value > 9;
  3. }) // 10

数组实例的 findIndex 方法的用法与 find 方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回 -1。

  1. [1, 5, 10, 15].findIndex(function(value, index, arr) {
  2. return value > 9;
  3. }) // 2

接受第二个参数

  1. function f(v){
  2. return v > this.age;
  3. }
  4. let person = {name: 'John', age: 20};
  5. [10, 12, 26, 15].find(f, person); // 26

另外,这两个方法都可以发现 NaN,弥补了数组的 indexOf 方法的不足。

  1. [NaN].indexOf(NaN)
  2. // -1
  3. [NaN].findIndex(y => Object.is(NaN, y))
  4. // 0

上面代码中,indexOf 方法无法识别数组的 NaN 成员,但是 findIndex 方法可以借助 Object.is 方法做到。

10. 数组的迭代

每个方法都接受两个参数:

  • 在每一项上运行的函数
  • 可选的,运行在该函数的作用域对象——影响 this 的值
  • 运行函数接受的参数
    • 数组项的值 item
    • 该项的位置 index
    • 数组对象本身 arr

10.1 every() 是否所有都符合

  • 作用:对数组中的每一项运行给定函数,判断是否所有都符合
  • 参数:对每一项调用的函数
  • 返回值:如果该函数对每一项都返回 true,则返回 true,否则返回 false
  • 原数组不变
  1. var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
  2. var everyResult = numbers.every(function(item, index, arr) {
  3. return item > 2;
  4. });
  5. everyResult //=> false

10.2 some() 是否有符合的

  • 作用:对数组中的每一项运行给定函数,判断是否有符合的
  • 参数:对每一项调用的函数
  • 返回值:如果该函数对每一项都返回 false,则返回 false,否则返回 true
  • 原数组不变

every 相对,some 只需要有符合的就会返回 true

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
var someResult = numbers.some(function(item, index, arr) {
  return item > 2;
});

someResult; //=> true

10.3 filter() 过滤掉不符合的

  • 作用:以特定的规则过滤数组成员
  • 参数:对每一项调用的函数
  • 返回值:调用函数返回 true 的成员组成的数组
  • 原数组不变

返回的是包含遍历中返回 true 的项的数组

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
var filterResult = numbers.filter(function(item, index, arr) {
  return item > 2;
});

filterResult //=> [3, 4, 5, 4, 3]

10.4 forEach() 遍历

  • 作用:单纯的遍历数组
  • 参数:对每一项运行的函数
  • 返回值:无
  • 原数组不变
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
numbers.forEach(function(item, index, arr) {
  //=> 执行一些操作
});

10.5 map() 运行结果组成的数组

  • 作用:对数组中的数据进行再处理、建立映射
  • 参数:对数组中的每一项运行给定函数
  • 返回值:返回每次函数返回的结果组成的数组
  • 原数组不变

返回的数组由给定函数的执行所返回的结果组成

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
var mapResult = numbers.map(function(item, index, arr) {
  return item * 2;
});
mapResult //=> [2, 4, 6, 8, 10, 8, 6, 4, 2]

11. 数组的归并

  • 作用:迭代所有项,然后构建一个最终返回值
  • 参数:每一项上调用的函数,可选的作为归并的初始值
  • 返回值:函数内构建的最终返回值
  • 原数组不变

接受两个参数

  • 在每一项上调用的函数
  • 可选的,作为归并基础的初始值,也就是第一个 prev
  • 调用的函数接受的参数,前一个项的函数返回的值 prev、当前项的值 cur、项的索引 index、数组对象 array

11.1 reduce()

从第一项开始归并

var values = [1, 2, 3, 4, 5];
var sum = values.reduce(function(prev, cur, index, array) {
  return prev + cur;
});
alert(sum); //15

// 第二个参数例子
const bifurcate = (arr, filter) =>
  arr.reduce(
    (acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc),
    [[], []]
  );

原理:

Array.prototype.reduce = function(reducer, initVal) {
    for (let i = 0; i < this.length; i++) {
    initVal = reducer(initVal, this[i], i, this);
  }
  return initVal;
}

11.2 reduceRight()

从最后一项开始归并,与 reduce() 方法类似