章节索引 :

JavaScript 数组

数组是一种使用整数作为键 (integer-key-ed) 属性和长度 (length) 属性之间关联的常规对象。此外,数组对象还继承了 Array.prototype 的一些操作数组的便捷方法。——MDN

数组可以存放多个值。数组的不是指数字,而是指数据,数组可以存放任意数据类型的值。

从理论上讲,在内存允许的情况下,数组的长度是无限的。

图片描述

1. 创建数组

数组由中括号 [] 包裹,每一项之间用逗号 , 分隔。

[第一项, 第二项, 第三项, ... , 第n项]

创建数组最常见的方式是使用字面量。

var hobby = ['java', 'c', 'c++', 'python', 'javascript', 1, 2, 3, true, false];

console.log(hobby);

通过直接描述一个数组的方式就可以创建一个数组。

2. 数组长度

数组的长度可以通过 length 属性获取。

var arr = [1, 2, 3, 4, 5];

console.log(arr.length); // 5

3. 访问数组成员

数组的每一项可以称之为成员

数组成员可以通过下标访问,下标也可以称为索引

下标可以理解成数组成员的编号,编号从 0 开始,到数组长度 -1 。

var arr = ['第一项', '第二项', '第三项'];

var item1 = arr[0]; // 从0开始编号 第一项的下标是0
var item2 = arr[1];
var item3 = arr[2];

console.log(item1); // 输出:第一项
console.log(item2); // 输出:第二项
console.log(item3); // 输出:第三项

通过数组[下标] 的方式即可访问到成员。

4. 遍历数组

数组遍历主要有两种方式:

  • for 循环
  • forEach 方法

4.1 for 循环

使用 for 循环,从数组下标 0 循环到最后一位,过程中通过下标访问成员。

var arr = ['第一项', '第二项', '第三项', '第四项', '第五项'];

var i, len;
for (i = 0, len = arr.length - 1; i <= len; i++) {
  console.log(arr[i]);
}

4.2 forEach 方法

forEach 是数组原型上的方法,所有数组都具有此方法。

forEach 接收一个函数作为参数,在遍历每一项的时候,这个函数会被调用,同时将当前遍历到的项、当前项的下标(索引)、遍历的数组作为函数参数传递过来。

var arr = ['第一项', '第二项', '第三项', '第四项', '第五项'];

arr.forEach(function(item, index, arr) {
  console.log('第' + (index + 1) + '项的值是:' + item);
});

第二个参数的值也是从 0 开始的。

通常第三个参数用到的比较少,没有用到可以没必要接收这个参数。

5. 修改数组成员的值

成员的值可以通过数组[下标] = 值的方式进行修改。

var arr = ['一', '三', '三', '四'];

console.log(arr[1]); // 输出:"三"

arr[1] = '二';

console.log(arr[1]); // 输出:"二"

6. 增减数组项

数组提供了很多方式来对成员进行增减操作,也可以依靠其他特性来增加减少数组成员。

6.1 增加

6.1.1 直接给指定位置赋值

通过下标,直接访问到一个不存在的成员,然后进行赋值,就可以为数组增加一项。

var arr = ['jquery', 'react'];

arr[1] = 'vue';
arr[2] = 'angular';

console.log(arr[2]); // 输出:"angular"
console.log(arr.length); // 输出:3

6.1.2 push 方法

push 方法接收任意个参数,这些参数会依次添加到数组的末尾,添加完后返回数组新的长度。

var arr = [1];

var length = arr.push(2, 3);

console.log(arr); // 输出:[1, 2, 3]
console.log(length); // 输出:3

通常不会用到这个返回的长度,可以不需要接收返回值。

6.1.3 unshift 方法

unshift 接收任意个参数,这些参数会被添加到数组头部,添加完后返回数组新的长度。

var arr = [3];

var length = arr.unshift(1, 2);

console.log(arr); // 输出:[1, 2, 3]
console.log(length); // 输出:3

6.2 删除

6.2.1 pop 方法

pop 方法会删除数组最后一项,并将删除项作为返回值。

var arr = ['c++', 'java', 'javascript'];

var lastOne = arr.pop();

console.log(lastOne); // 输出:"javascript"

如果数组是空的,调用 pop 会返回 undefined

6.2.2 shift 方法

shift 方法会删除数组的第一项,并将删除项作为返回值。

var arr = ['996', '007'];

const first = arr.shift();

console.log(first); // 输出:"996"

与 pop 一样,如果是数组为空的情况下,会返回 undefined

6.3 在数组中间删除或添加值

splice 方法可以在任意位置添加或删除值。

这个方法接受任意个参数,前两个为从哪里开始(从 0 开始计数),删除几个,从第三个开始则是要添加的项。

arr.splice(从第几个开始, 要删除几个, 要添加的项目1, 要添加的项目2, ..., 要添加的项目n);

如果不需要删除,只需要往数组中间插入值,只需要传递 0 给第二个参数即可。

// 在第二项之后插入3, 4, 5
var arr = [1, 2, 6];

arr.splice(2, 0, 3, 4, 5);

因为第一个参数是从 0 开始计数,所以在第二项之后,就是要插入在第三项的位置,所以传递 2 ,不需要删除项目,所以第二个参数传递 0 ,之后就是要插入的项。

注意:往数组中间插入数据的情况相对较少,数组做这种操作是比较低效的,小量的操作对性能的影响可以忽略不计,但有超大量非首尾的增减操作,应考虑使用链表

删除项只需要指定从第几项开始删除,要删除几项即可。

// 去除 '996'、'加班'
var arr = ['早睡早起', '朝九晚五', '996', '加班'];

arr.splice(2, 2);

'996’和’加班’是连续的,所以一个 splice 就可以删除掉这 2 项,2 个参数的意思就是从第 2 项开始,删除 2 项。

7. 清空数组

将数组所有成员全部删除就达到了清空的效果。

var arr = [1, 2, 3, 4];

arr.splice(0, arr.length);

当然也可以使用 pop 一个个删除,但是通常都不会用这种方式。

清空数组最常用的方式是重新给变量赋值。

var arr = ['red', 'green', 'blur'];

arr = [];

console.log(arr); // 输出空数组:[]

通过给变量赋值一个新的空数组,达到清空数组的目的,但是这样会改变引用,新赋值的数组和之前的数组并不是同一个。

另一种方式可以让保持对当前数组的引用。

var arr = ['yellow', 'black'];

arr.length = 0;

通过给数组的 length 属性重新赋值,也可以达到清空数组的效果。

这种方式相对灵活,假如需求是保留三项、五项,只需要给 length 赋值对应的值即可。

8. 使用 Array 创建数组

内建对象 Array 也可以用来创建数组。

var arr = new Array();

如果什么参数都不传递,则返回一个空数组。

传参则有 2 种情况:

  • 如果只传一个参数,并且这个参数的类型为数字,则会创建长度为这个数字的数组;
  • 传入其他类型的一个或者多个参数,则会将这些参数组合成数组。
var arr = new Array(10);

console.log(arr); // 输出:[empty × 10]
console.log(arr.length); // 输出:10

在控制台可以观察到这个数组长度为 10,但均为 empty

如果尝试着访问其中某一项,得到的值是 undefined

var arr1 = new Array('item1');

var arr2 = new Array(1, 2, 'item3');

console.log(arr1); // 输出:["item1"]
console.log(arr2); // 输出:[1, 2, "item3"]

这样创建的数组,成员与传参一致。

9. 数组中的 undefined 与 empty

在数组中 undefined 与 empty 是有区别的。

使数组项变成 empty 通常有两种方式。

  • 使用 Array 对象同时提供了长度创建出来的数组
  • 使用 delete 关键字删除的数组项
var arr1 = new Array(10);

arr1[0] = 1;

var arr2 = [1, 2, 3, 4, 5];

delete arr2[3];

console.log(arr1);
console.log(arr2);

图片描述

虽然 empty 的项在访问的时候返回的是 undefined ,但其本身只做简单占位,\ 是遍历不到的。

var arr = [1, undefined, 3, 4, 5];

delete arr[3];

arr.forEach(function(item, index) {
  console.log(index, item);
});

var i = 0;
for (i in arr) {
  console.log(i, arr[i]);
}

上面两种遍历的方式都是遍历不到 empty 项的,而 undefined 是可以遍历到的,这是最主要的区别。

10. 使用数组

数组非常常用,大量的 HTML 字符串在做拼接的时候,就会使用到数组。

除了用于简单的存储数据,数组也可以被用来解决问题。

10.1 判断括号是否匹配

判断一个数学式子的括号匹配是否合法,如 (1 + 2) * (3 + 4)) ,这个式子就是不合法的。

校验括号合法不单单要看左括号和右括号的数量是否相等,还要看括号的顺序, ))(( 这样的括号顺序一定是错误的。

利用 JavaScript 数组的特性,可以很容易的实现。

// 空数组
var stack = [];

// 一个式子
var equation = '(1 + (2 - 3) * 4) / (1 - 3)';

var i, len;
for (i = 0, len = equation.length; i < len; i++) {
  if (equation[i] === '(') { // 如果碰到左括号
    // 往数组里放个1
    stack.push(1);
  } else if (equation[i] === ')') { // 如果碰到右括号
    if (!stack.length) { // 判断数组长度,如果是0,则肯定是出错的,数组长度0的时候说明没有左括号,没有左括号是不可能出现右括号的
      // 随便放一个1
      stack.push(1);
      break;
    }
    // 如果数组不是空的 就从数组末尾拿一个走。
    stack.pop();
  }
}

// 判断数组长度
if (!stack.length) { // 如果数组已经空了,说明括号都一一对应,式子里的括号没问题。
  console.log('括号合法');
} else {
  console.log('括号不合法');
}

使用数组实现的具体思路就是,碰到左括号就往数组里放一个成员,碰到一个右括号就拿掉一个成员。

这样如果最后有剩下,说明括号没有一一成对。

(1+2*(3+4))*1 如这样一个式子:

初始化操作:
  定义数组 arr为空

从式子第一个字符开始循环
  第一次循环:
    数组的值为 []
    得到字符"("
    判断是左括号,往数组里放一个1,表示碰到了左括号

  第二次循环
    数组的值为 [1]
    得到字符"+"
    既不是左括号,又不是右括号,进行下一轮循环,不做操作

  第三次循环
  第四次循环
  第五次循环
    与第二次循环基本一致

  第六次循环
    数组的值为 [1]
    得到字符"("
    是左括号 往数组里再放一个1,表示碰到了左括号

  第七次循环
    数组值为 [1, 1]
    得到字符"3"
    不是左括号,也不是右括号,进行下一轮循环

  第八次循环
  第九次循环
    与第七次一致

  第十次循环
    数组的值为 [1, 1]
    得到字符")"
    是右括号,从数组末尾拿掉一项

  第十一次循环
    数组的值为 [1]
    得到字符")"
    是右括号,从数组末尾拿掉一项

  第十二次循环
  第十三次循环
    数组值为 []
    都不是括号,不做操作
循环结束

判断数组值,如果是空的,说明括号匹配完了,显然 (1+2*(3+4))*1 是合法的

当然这种判断有局限性,假如碰到 ((1+)2)-3 这种非括号造成不合法的式子,就判断不出来了。

其实这里用到了这种数据结构,这个问题在栈的应用上很经典,是算法入门常见面试题之一。

11. 类数组

类数组并不是数组,而是长得像数组的对象。

var fakeArray = {
  0: '第一项',
  1: '第二项',
  3: '第三项',
  length: 3,
};

console.log(fakeArray[0]); // 输出:"第一项"
console.log(fakeArray.length); // 输出:3

上述例子中的 fakeArray 就是一个类数组,属性是以类型数组的下标的形式存在,同时也具有 length 属性。

这种类数组对象,也被称为 array-like对象 ,部分文献也称为伪数组

类数组对象可以转化为数组,许多方法在设计时也会考虑支持类数组。

12. 小结

JavaScript 中的数组非常灵活,可以存放任意类型、任意长度 (内存足够的情况下) 的数据,其下标从 0 开始,最大到数组长度减去 1 ,并提供了一系列方法,来完成增、删、改、查操作。

数组项的 emptyundefined 的区别,是面试中常问的问题。

前置知识
什么是JavaScript 开发与学习环境准备 调试方案
基础
JavaScript 变量 JavaScript 数据类型 JavaScript if 语句 JavaScript for 语句 JavaScript 算数运算符 JavaScript 比较运算符 JavaScript 逻辑运算符 JavaScript 表达式 JavaScript 函数 JavaScript 对象 JavaScript 字符串 JavaScript 数字 JavaScript 数组 JavaScript switch 语句 JavaScript while 语句 JavaScript break与continue JavaScript with document.cookie
内置对象
JavaScript Function JavaScript Math JavaScript Date JavaScript RegExp JavaScript JSON
JavaScript 与 DOM
什么是DOM DOM和JavaScript的关系 获取和操作 DOM 节点 JavaScript DOM与事件 JavaScript DOM 事件绑定 JavaScript DOM 事件对象 JavaScript DOM 事件流 JavaScript DOM 事件优化 JavaScript DOM 自定义事件
表单处理
使用 JavaScript 校验表单
BOM
BOM window 对象 常用的 BOM 相关对象 BOM 常用属性和方法
AJAX
JavaScript AJAX
进阶知识
JavaScript 异常处理 JavaScript 三元运算符 JavaScript 逗号操作符 JavaScript void JavaScript typeof JavaScript delete JavaScript debugger JavaScript getter &setter JavaScript 原型 JavaScript new操作符和构造函数 JavaScript instanceof JavaScript this JavaScript 严格模式 JavaScript 作用域 JavaScript 闭包 JavaScript 变量提升 JavaScript 对象包装器
常用库
jQuery Lodash moment.js swiper
进阶指南
ECMAScript6 Node.js Babel CSS 预处理器 代码规范 TypeScript Web Components 小程序 Vue / React / Angular JavaScript 关键字
常见疑点与误区
分号问题 对象属性访问问题 this 使用问题 浮点数精度问题 独一无二的 NaN 避免全局污染 控制台观察对象问题 根据环境选择语言特性
扩展
相关资源