0. 前言
不管采用什么样的编程语言进行编程,它的组成部分都是数据 + 算法;而数组是代码中用来存储数据的重要方式。因此,对于 JavaScript 这门编程语言来说,数组是极其重要的部分。在这篇文章中,小编就来跟大家唠一唠JavaScript数组那些事儿。
在JavaScript中,有某些类型(例如字符串)可以利用数组的方式来进行索引,但需要注意的是它们并不是数组。在这篇文章中,我们就来聊一聊数组和类数组对象。
1. 数组
由于JavaScript语言是一种弱类型编程语言,它的数据类型并不像Java、c++这样的强类型编程语言区分的那么明显。因此,在进行数组定义的时候,我们并不需要直到数组中保存的是什么类型的元素,只要知道这是一段存储空间,用来保存数据的就可以了。
在开始介绍数组之前,我们先来看看如何判断一个对象是不是数组对象。ECMAScript语言提供了两种检测是否为数组的方法:
instanceof关键字,语法为value instanceof Array.这行代码的含义为某个对象构造函数是否为Array,如果是,则返回true;否则返回false。Array.isArray()方法,语法为Array.isArray(value)。这行代码是Array对象的方法,其目的在于确定一个值是否为数组,而不用管他是在哪个全局执行上下文中创建的。
1.1 创建数组
创建数组的方式有两种,一种是利用构造函数进行创建,一种是直接将数组元素用大括号包裹起来。
- 构造函数:使用构造函数创建数组时需要注意的是,是否带
new关键字并不影响数组的创建。请看下面的例子:
// 利用构造函数创建空数组
let color = new Array();
let color = Array();
// 利用构造函数创建长度为4的数组
let color = new Array(4);
let color = Array(4)
// 利用构造函数创建包含三个字符串的数组
let color = new Array('red', 'blue', 'green');
let color = Array('red', 'blue', 'green');
- 大括号:
let colors = ["red", "blue", "green"]; // 创建一个包含 3 个元素的数组
let names = []; // 创建一个空数组
Array 构造函数还有两个 ES6 新增的用于创建数组的静态方法:from() 和 of()。from()用于将类数组结构转换为数组实例,of()用于将一组参数转换为数组实例。
Array.from()的第一个参数是一个类数组对象,即任何可迭代的结构,或者有一个 length 属性和可索引元素的结构。
console.log(Array.from("Matt")); // ["M", "a", "t", "t"]
// 可以使用 from()将集合和映射转换为一个新数组
const m = new Map().set(1, 2)
.set(3, 4);
const s = new Set().add(1)
.add(2)
.add(3)
.add(4);
console.log(Array.from(m)); // [[1, 2], [3, 4]]
console.log(Array.from(s)); // [1, 2, 3, 4]
// Array.from()对现有数组执行浅复制
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1);
console.log(a1); // [1, 2, 3, 4]
alert(a1 === a2); // false
// 可以使用任何可迭代对象
const iter = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
yield 4;
}
};
console.log(Array.from(iter)); // [1, 2, 3, 4]
// arguments 对象可以被轻松地转换为数组
function getArgsArray() {
return Array.from(arguments);
}
console.log(getArgsArray(1, 2, 3, 4)); // [1, 2, 3, 4]
// from()也能转换带有必要属性的自定义对象
const arrayLikeObject = {
0: 1,
1: 2,
2: 3,
3: 4,
length: 4
};
console.log(Array.from(arrayLikeObject)); // [1, 2, 3, 4]
Array.of() 可以把一组参数转换为数组。
console.log(Array.of(1, 2, 3, 4)); // [1,2,3,4]
console.log(Array.of(undefined)); // [undefined]
1.2 稀疏数组和数组长度
稀疏数组就是包含从 0 开始的不连续索引的数组。通常数组的length属性值代表了数组中元素的个数。但如果数组是稀疏的,length属性值大于元素的个数。我们可以通过利用构造函数创建数组时指定数组的长度来创建稀疏数组,也可以使用 , 做占位符来创建稀疏数组。
let a = [, , ,]; // 长度为 3 的稀疏数组
let a = new Array(4); // 长度为 4 的稀疏数组
ES6 规范重新定义了该如何处理稀疏数组中的空位。ES6 新增的方法和迭代器与早期 ECMAScript 版本中存在的方法行为不同。 ES6 新增方法普遍将这些空位当成存在的元素,只不过值为 undefined。ES6 之前的方法则会忽略这个空位,但具体的行为也会因方法而异。
每一个数组都会有一个length属性,就是这个属性使其区别于常规的JavaScript对象,对于非稀疏数组而言,length属性值代表了数组中元素的个数,它的值比索引值大1(因为索引是从0开始的)。当数组是稀疏数组时,length属性值大于元素个数。
需要注意的是对一个数组元素使用delete不会修改数组的length属性,也不会将元素从高索引处移下来填充已删除的空白。如果从数组中删除一个元素,它就变成稀疏数组。相反,我们可以简单地设置length属性组为一个新的期望长度来删除数组尾部的元素。
1.3 数组方法
《JavaScript高级程序设计》对数组的方法分了十个类别。在这一部分的最后,我们就来根据这十类来介绍一下数组的方法。
1.3.1 迭代器方法
这个方法时ES6新增的方法,用来检索数组的内容,它包含三个方法:
keys():返回数组索引的迭代器values():返回数组元素的迭代器entries():返回索引/值对的迭代器
1.3.2 复制和填充方法
ES6 新增了两个方法:
- 批量复制方法:
copyWithin():会按照指定范围浅复制数组中的部分内容,然后将它们加入到指定索引开始的位置 - 填充数组方法:
fill()传入1~3个参数:- 传入一个参数的情况:所有的元素都填充某个值
- 传入两个参数的情况:用第一个参数的值填充大于等于索引第二个参数的元素
- 传入三个参数的情况:第二个参数和第三个参数构成一个左闭右开的区间,用第一个参数的值去填充这个区间的元素。允许负索引,并且如果区间越界了则忽略
1.3.3 转换方法
一般的转换方法有三个,并不是ES6定义的:
toLocaleString()toString()valueOf()
如果想采用不同的分隔符进行转换的话,可以使用join()方法
1.3.4 栈方法
栈方法就是很经典的:
push()pop()
1.3.5 队列方法
队列方法就是在数组的开头删除一个元素,数组长度减一。
shift()
与它对应的方法是:unshift()
1.3.6 排序方法
sort():接受一个比较函数,根据比较函数定义的比较规则进行原地排序reverse():将数组进行原地翻转
1.3.7 操作方法
concat():将两个数组进行拼接slice():获取数组对应索引的元素,并且返回新数组splice():对原数组进行增删改
1.3.8 索引和位置方法
indexOf():获取某个元素的索引,如果没找到该元素,则返回-1lastIndexOf():获取某个元素在数组中最后一次出现的索引include():在数组中查找某个元素,并返回布尔值find()和findIndex():传入一个判断条件的函数,返回第一个匹配的元素或者第一个匹配的元素的索引。
1.3.9 迭代方法
every():对数组的每一项都运行传入的函数,如果对每一项都返回true,则这个方法返回truefilter():对数组的每一项都运行传入的函数,函数返回true的项会组成数组之后返回forEach():对数组的每一项都运行传入的函数,没有返回值map():对数组的每一项都运行传入的函数,返回由每次函数调用的结果构成的数组some():对数组的每一项都运行传入的函数,只要有一项函数返回true,则这个方法返回true
1.3.10 归并方法
reduce():从第一项遍历到最后一项,迭代数组所有的项,并在此基础上构建一个最终返回值reduceRight():从最后一项开始遍历到第一项,迭代数组所有的项,并在此基础上构建一个最终返回值
2. 类数组
在JavaScript中,我们有时候把拥有一个数值length属性和对应非负整数属性的对象看做一种类型的数组,我们把这些对象称为类数组对象。对于这些类数组对象而言,虽然不能在他们之上直接调用数组方法或者期望length属性有什么特殊行为,但是仍然可以用针对真正数组的遍历代码来遍历它们。
总结一下,类数组对象有两个特点:
- 有一个数值的length属性
- 属性的键是非负整数
对于类数组对象,我们不能直接调用数组方法,也不能通过length指定长度,但是可以使用数组的遍历方法去遍历
例子:
// 类数组对象
let a = {
0: 'aaa',
1: 'bbb',
2: 'ccc',
3: 'ddd',
length: 4,
};
console.log(a instanceof Array); //false
for(let index in a) {
console.log(a[index]);
} // aaa,bbb,ccc,ddd,4
此外,字符串也是类数组对象。