1.数组基础

1.1 数组的概念

  • 数组可以把一组相关的数据一起存放,并提供方便的访问(获取)方式。
  • 数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式。

1.2 创建数组

JS 中创建数组有两种方式:

  • 利用 new 创建数组

    1. var 数组名 = new Array()
    2. var arr = new Array(); // 创建一个新的空数组


    注意 Array () ,A 要大写

  • 利用数组字面量创建数组

    1. //1. 使用数组字面量方式创建空的数组
    2. var 数组名 = [];
    3. //2. 使用数组字面量方式创建带初始值的数组
    4. var 数组名 = ['小白','小黑','大黄','瑞奇'];
  • 数组的字面量是方括号 [ ]
  • 声明数组并赋值称为数组的初始化
  • 这种字面量方式也是我们以后最多使用的方式
    • 数组元素的类型
      数组中可以存放任意类型的数据,例如字符串,数字,布尔值等。
      1. var arrStus = ['小白',12,true,28.9];

      出现的问题:
  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 个项。

1.3 获取数组中的元素

索引 (下标) :用来访问数组元素的序号(数组下标从 0 开始)。

图片1.png

数组可以通过索引来访问、设置、修改对应的数组元素,可以通过“数组名[索引]”的形式来获取数组中的元素。

  1. // 定义数组
  2. var arrStus = [1,2,3];
  3. // 获取数组中的第2个元素
  4. alert(arrStus[1]);

注意:如果访问时数组没有和索引值对应的元素,则得到的值是undefined

1.4 遍历数组

  • 数组遍历
    把数组中的每个元素从头到尾都访问一次(类似学生的点名),可以通过 for 循环索引遍历数组中的每一项
  1. var arrStus = ['red','green', 'blue'];
  2. for(var i = 0; i < arr.length; i++){
  3. console.log(arrStus[i]);
  4. }
  • 数组的长度
    数组的长度:默认情况下表示数组中元素的个数
    使用“数组名.length”可以访问数组元素的数量(数组长度)。

    1. var arrStus = [1,2,3];
    2. alert(arrStus.length); // 3


    注意

  • 此处数组的长度是数组元素的个数 ,不要和数组的索引号混淆。

  • 当我们数组里面的元素个数发生了变化,这个 length 属性跟着一起变化
  • 数组的length属性可以被修改:
    • 如果设置的length属性值大于数组的元素个数,则会在数组末尾出现空白元素;
    • 如果设置的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

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

1.5 数组中新增元素

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

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

1.6. 数组的检测

instanceof 操作符

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

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

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

Array.isArray 是否是数组

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

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

练习:

  1. <script>
  2. // 将数组 [2, 0, 6, 1, 77, 0, 52, 0, 25, 7] 中大于等于 10 的元素选出来,放入新数组。
  3. // 1、声明一个新的数组用于存放新数据newArr。
  4. // 2、遍历原来的旧数组, 找出大于等于 10 的元素。
  5. // 3、依次追加给新数组 newArr。
  6. // 方法1
  7. var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
  8. var newArr = [];
  9. var j = 0;
  10. for (var i = 0; i < arr.length; i++) {
  11. if (arr[i] >= 10) {
  12. // 新数组索引号应该从0开始 依次递增
  13. newArr[j] = arr[i];
  14. j++;
  15. }
  16. }
  17. console.log(newArr);
  18. // 方法2
  19. var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
  20. var newArr = [];
  21. // 刚开始 newArr.length 就是 0
  22. for (var i = 0; i < arr.length; i++) {
  23. if (arr[i] >= 10) {
  24. // 新数组索引号应该从0开始 依次递增
  25. newArr[newArr.length] = arr[i];
  26. }
  27. }
  28. console.log(newArr);
  29. </script>

2.数组方法

方法列表

下列每个例子中的问题默认为独立的

2.1 数组转换字符串

2.1.1 三种转换

toLocaleString()toString()valueOf()

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

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

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

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

2.1.2 join 连接

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

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

  1. var arr = [12,23,34];
  2. arr.join('+'); //=> '12+23+34'
  3. eval(arr.join('+')); //=> 69
  4. console.log(arr.join(''));
  5. // 输出: "122334"
  6. console.log(arr.join('-'));
  7. // 输出: "12-23-34"

2.2 数组的增删改

2.2.1 push 从后增加

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

arr.push(45, 56); //=> 5 arr //=> [12,23,34,45,56]

  1. `push()` `pop()` 属于栈方法
  2. <a name="bOJEt"></a>
  3. #### 2.2.2 pop 从后删除
  4. - **作用**:删除数组的最后一项
  5. - **参数**:无
  6. - **返回**:被删除的那一项内容
  7. - 原有数组**改变**
  8. ```javascript
  9. var arr = [12, 23, 34];
  10. arr.pop(); //=> 34
  11. arr //=> [12,23]

2.2.3 shift 从前删除

  • 作用:删除数组的第一项
  • 参数:无
  • 返回:被删除的那一项内容
  • 原有数组改变
    1. var arr = [12, 23, 34];
    2. arr.shift(); //=> 12
    3. arr //=> [23,34]
    基于 shift 方法删除数组中的第一项,第一项被删除后,原有后面的每一项的索引都要向前减 1(往前提一位),称为数组塌陷

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

2.2.4 unshift 从前增加

  • 作用:向数组开始位置追加内容
  • 参数:追加内容,任意个项
  • 返回值:新增后数组长度
  • 原有数组改变

    1. var arr = [12, 23, 34];
    2. arr.unshift(10, 11); //=> 5
    3. arr //=> [10,11,12,23,34]

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

    2.3 splice 增加、删除、替换

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

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

    2.3.1删除

  • arr.splice(index, howmany)

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

arr.splice(1); //=> [23,67] arr //=> [12]

  1. > **注意**:基于 splice 的删除,会造成数组塌陷的问题,也就是后面项的索引会向前补全,原先项的索引改变
  2. ---
  3. <a name="oPOYi"></a>
  4. #### 2.3.2插入
  5. - `splice(index, 0, [item1, item2,...])`
  6. - 指定三个以上参数,从索引 index 开始,删除 0 项,把后面的参数插入到数组中,索引 index 的前面
  7. - **返回**:被删除的部分,也就是空数组
  8. - 原有数组**改变**
  9. ```javascript
  10. var arr = [12,23,34,45,56,67];
  11. arr.splice(2,0,'aa','bb'); //=> []
  12. arr //=> [12,23,'aa','bb',34,45,56,67]

2.3.3替换

  • 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]

    2.4. 数组的克隆

    2.4.1 slice 抽取

  • 作用:在一个数组中,按照条件查找出数组中的部分内容,基于这些内容创建一个新数组。

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

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

实现数组的克隆

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

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

支持负数索引

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

    2.5 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)

2.6. 数组的重排序

2.6.1 reverse 反转

  • 作用:反转数组项的顺序
  • 参数:无
  • 返回:排序后的数组
  • 原有数组改变

    1. var arr = [12,23,34];
    2. arr.reverse(); //=> [34,23,12]

    2.6.2 sort 自定义排序

  • 作用:给数组排序

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

sort 不传递参数的排序

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

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

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

  • 如果a-b<0则返回一个负数,交换位置
  • 如果a,b相等则返回 0
  • 如果a-b>0则返回一个正数,不交换位置

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

2.6.3数字的排序

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

2.7 数组的查找

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

2.7.1 indexOf 从头开始查找

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

    1. indexOf(searchValue, [fromIndex]);

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

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

    2.7.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 方法做到。

2.8. 数组的迭代

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

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

2.8.1 every() 是否所有都符合

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

everyResult //=> false

  1. <a name="DxxA4"></a>
  2. #### 2.8.2 some() 是否有符合的
  3. - **作用**:对数组中的每一项运行给定函数,判断是否有符合的
  4. - **参数**:对每一项调用的函数
  5. - **返回值**:如果该函数对每一项都返回 `false`,则返回 `false`,否则返回 `true`
  6. - 原数组**不变**
  7. 与 `every` 相对,`some` 只需要有符合的就会返回 `true`
  8. ```javascript
  9. var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
  10. var someResult = numbers.some(function(item, index, arr) {
  11. return item > 2;
  12. });
  13. someResult; //=> true

2.8.3 filter() 过滤掉不符合的

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

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

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

2.8.4 forEach() 遍历

  • 作用:单纯的遍历数组
  • 参数:对每一项运行的函数
  • 返回值:无
  • 原数组不变

    1. var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    2. numbers.forEach(function(item, index, arr) {
    3. //=> 执行一些操作
    4. });

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

  • 作用:对数组中的数据进行再处理、建立映射

  • 参数:对数组中的每一项运行给定函数
  • 返回值:返回每次函数返回的结果组成的数组
  • 原数组不变

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

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

2.9. 数组的归并

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

接受两个参数

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

2.9.1 reduce()

从第一项开始归并

  1. var values = [1, 2, 3, 4, 5];
  2. var sum = values.reduce(function(prev, cur, index, array) {
  3. return prev + cur;
  4. });
  5. alert(sum); //15
  6. // 第二个参数例子
  7. const bifurcate = (arr, filter) =>
  8. arr.reduce(
  9. (acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc),
  10. [[], []]
  11. );

原理:

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

2.9.2 reduceRight()

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