05.JavaScript基础系列:数组

218 阅读10分钟

JavaScript基础系列

核心API

不改变原数组的 ECMAScript3 API

  • join(): 将数组中素有元素转换为字符串
  • slice(): 不改变原数组,返回指定数组的一个片段或子数组,是位置,不是长度
  • concat(): 不改变原数组,创建并返回一个新数组,如果参数中有数组,连接的是数组中的元素,不会递归扁平化数组的数组

改变原数组的 ECMAScript3 API

  • reverse(): 数组逆向操作,是通过采取替换方式在原始数组上进行操作
  • sort(): 将数组中的元素排序并返回排序后的结果,默认是按字母顺序排序,如果包含 undefined,会被排到最后
  • splice(): 在数组中插入或删除元素,会修改原始数组,返回删除元素组成的数组,是个数,不是位置
  • push()
  • pop()
  • unshift()
  • shift()

ECMAScript 5 API

提供的方法不会修改原始数组,但是传递给这些方法的函数可以修改数组

  • forEach(): 遍历数组,无法中途提前终止遍历;如想提前终止,可以通过 throw 异常退出
  • map(): 将调用数组的每个元素传递给指定的函数,并返回一个数
  • filter(): 调用的数组的一个子集,传递的函数判断逻辑是用 true 或 false,会跳过洗漱数组中缺少的元素
  • every(): 所有的元素进行判断都满足才返回 true,空数组返回true
  • some(): 有一个元素进行判断满足就返回 true,空数组返回true
  • reduce():使用指定的函数将数组元素进行组合,生成单个值
  • indexOf()
  • lastIndexOf()

数组常用的API

  • 数组是值的有序集合,每个值叫做一个元素,而每个元素在数组中都有一个位置,以数字表示,成为索引
  • 数组是无类型的,数组元素可以是任意类型的
  • 数组是动态的,会根据需要自动增长或缩减
  • 数组元素的索引可以不连续,中间可以存在空缺
  • 数组继承自 Array.prototype 中的属性,具有丰富的操作方法

1.创建数组

  • 使用数组直接量是创建数组最简单的方法,可在方括号中将数组元素用逗号隔开即可
  • 调用构造函数 Array() 创建数组
# 直接量
var a1 = [1, 2, 3]
var a2 = [1, , 2]           // 数组有三个元素,中间元素为 undefined

# 构造函数
var a3 = new Array()        // 等价于 a3 = [];
var a4 = new Array(10)      // 指定数组的长度,但数组中没有存储值
var a5 = new Array(1, 2)    // 等价于 a5 = [1, 2]

2.数组元素的读和写

  • 使用负数或非整数来索引数组,这种情况下,数字转换为字符串,字符串作为属性名来用,即作为对象的属性而不是数组的索引
  • 使用非负整数的字符串,这种情况下,字符串转换为数字,作为数组索引使用
  • 使用小于 2^32 的非负数,这种情况下是作为数组索引使用
a[-1.23] = true;    // 创建名为 "-1.23" 的属性
a["1000"] = 0;      // 作为索引,索引值是 100
a[1.000]            // 等价于 a[1]

数组索引仅仅是对象属性名的一种特殊类型,这意味着 JavaScript 数组不存在越界的错误概念。当试图方法一个不存在属性时,只会得到 undefined。

3.稀疏数组

稀疏数组是包含从 0 开始的不连续索引的数组。可以通过 Array() 构造函数进行创建。

var a1 = new Array(3)   // 该数组根本没有元素
0 in a1;                // false, a1在索引0处没有元素
a1.length;              // 3

当省略数组直接量中的值时(使用连续的逗号,比如[1,,3]),这时所得到数组也是稀疏数组。

var a2 = [1,,3]         // 该数组根本没有元素
1 in a2;                // fasle, a2 在索引1处没有元素

var a3 = [undefined]
0 in a3;                // true, a3 在索引0处有一个值为 undefined 的元素

实际在我们所碰到的绝大多数 JavaScript 数组不是稀疏数组,并且,如果确实遇到稀疏数组,代码很可能像对待非稀疏数组一样对待它们,只不过它们包含了一些 undefined 值。

4.数组长度

  • 每个数组有一个 length 属性,就是这个属性使其区别于常规的 JavaScript 对象
  • 设置 length 属性为一个小于当前长度的非负整数 n 时,会从当前数组中删除索引值大于或等于 n 的元素
  • 可以通过 Object.defineProperty() 让数组的 length 属性变成只读
  • 可以通过 Object.seal() 或者 Object.freeze() 使数组元素不能配置,即不能对其删除

5.数组元素的添加和删除

  • 通过赋值的方式添加数组元素
  • 通过 push() 方法在数组末尾增加一个或多个元素
  • 通过 unshift() 方法在数组的首部插入一个元素
  • 通过 delete 运算符删除数组元素,不会改变数组的 length 属性
  • 通过 pop() 方法在数组末尾删除一个元素
  • 通过 shift() 方法在数组的首部删除一个元素

6.数组遍历

  • for: 最常用的方法
  • for/in: 不建议使用,能枚举出继承的属性名、无法保证其属性
  • forEach: 按照索引的顺序挨个传递给定义的一个个函数

7.多维数组

JavaScript 不支持真正的多维数组,但可以用数组的数组来近似,访问数组的数组中的元素,简单的使用两次 [] 操作符即可。

8.常规数组方法: ECMAScript 3

  • join(): 将数组中素有元素转换为字符串,是 String.split() 逆向操作
var a = [1, 2, 3];
a.join('');         //'123'
console.log(a);	    // [1, 2, 3]
  • reverse(): 数组逆向操作,是通过采取替换方式在原始数组进行操作
var a = [1, 2, 3];
a.reverse().join()      // '3,2,1'
console.log(a)          // 3,2,1'
  • sort(): 将数组中的元素排序并返回排序后的结果,默认是按字母顺序排序,如果包含 undefined,会被排到最后
var a = ['banner', undefined, 'apple', 'mango']
a.sort();
console.log(a)      // ["apple", "banner", "mango", undefined]
  • concat(): 创建并返回一个新数组,如果参数中有数组,连接的是数组中的元素,不会递归扁平化数组的数组,也不会修改原始数组
var a = [1, 2, 3]
var b = a.concat(4, [5, [6, 7]]);
console.log(a);     // [1, 2, 3]
console.log(b);     //[1, 2, 3, 4, 5, [6, 7]]
  • slice(): 返回指定数组的一个片段或子数组,不会修改原始数组
    • 第一个参数: 指定了数组的位置,包含
    • 第二参数: 指定了数组结束的位置,不包含,如省略即取数组长度
var a = [0, 1, 2, 3, 4];
console.log(a.slice(1, 3))      // [1,2]
console.log(a.slice(1))         // [1, 2, 3, 4]
console.log(a.slice(1, -1))     // [1, 2, 3]
  • splice(): 在数组中插入或删除元素,会修改原始数组,返回删除元素组成的数组
    • 第一个参数: 指定了要插入或删除的起始位置
    • 第二个参数: 指定了要删除元素的个数,如即删除从起点开始到尾部的元素
    • 第三个参数: 要插入的元素
var a = [0, 1, 2, 3, 4]
a.splice(2, 0, 5 , 6);          // []
console.log(a)                  // [0, 1, 5, 6, 2, 3, 4]

var a = [0, 1, 2, 3, 4]
a.splice(2, 3, [5 , 6], 7);     //[[5 , 6], 7]
console.log(a)                  // [0, 1, [5 , 6], 7]
  • push() 和 pop()

push() 和 pop() 允许把数组当做栈来使用。

  • unshift() 和 shift()

unshift() 和 shift() 行为非常类似于 push() 和 pop(),不一样的操作是在数组的头部而非尾部进行操作。

当使用多个参数调用 unshift() 时,参数是一次性插入(类似 splice() 方法),而非一次一个地插入。

  • toString() 和 toLocaleString()

等同于 join('') 操作,返回一个字符串。

9.高阶数组方法: ECMAScript 5

高阶数组方法包含映射、过滤、检测、简化和搜索方法,大多数方法操作如下:

# val: 数组元素
# index: 元素的所以
# a: 数组本身
arr.method((val, index, a) => {
    // TODO
})

ECMAScript 5 提供的方法不会修改原始数组,但是传递给这些方法的函数可以修改数组。

  • forEach(): 遍历数组,无法中途提前终止遍历;如想提前终止,可以通过 throw 异常退出
var data = [1, 2, 3, 4]
var sum = 0;
data.forEach((val) => sum+= val);
sum;            // 10

data.forEach((val, index, a) => a[index]= val + 1);
data;           // [2, 3, 4, 5]


data.forEach((val, index, a) => {
    try {
	    if (index === 2) throw new Error('test')
	    a[index]= val + 1;
	} catch(e) {
	    if (a.message === 'test') return;
	    throw e;
	}
});
data;           // [3, 4, 4, 5]
  • map(): 将调用数组的每个元素传递给指定的函数,并返回一个数组,包含该数组的返回值。返回的是新数组,不修改调用的数组
var data = [1, 2, 3, 4]
var b1 = data.map((val) => val * val);
data;       // [1, 2, 3, 4]
b1;         // [1, 4, 9, 16]

  • filter(): 调用的数组的一个子集,传递的函数判断逻辑是用 true 或 false,会跳过稀疏数组中缺少的元素
var data = [1, 2, , undefined, null, 3, 4]
var a = data.filter(val => val)
data;       //  [1, 2, , undefined, null, 3, 4]
a;          // [1, 2, 3, 4]

var a = data.filter(val => true)
a;          //  [1, 2, undefined, null, 3, 4]
  • evey() 和 some()

every() 和 some() 方法是数组的逻辑判定,对数组元素应用指导的函数进行判定,返回 true 或 false。

every() 所有的元素进行判断都满足才返回 true;some() 有一个元素进行判断满足就返回 true,这两个 API 确定返回值后就会停止遍历数组元素。

在空数组上调用 every() 返回 true, some() 返回 false。

  • reduce() 和 reduceRight()

使用指定的函数将数组元素进行组合,生成单个值。这个在函数式编程中是常见的操作,也可以称为 "注入" 和 "折叠"。

reduce() 需要两个参数

- 第一个是执行化简操作的函数,化简函数的任务就是用某种方法把两个组组合或简化为一个值,并返回化简后的值
- 第二个(可选) 是一个传递给函数的初始值
var a = [1, 2, 3, 4, 5]
var sum = a.reduce((x, y) => x + y, 0)          // 求和
var max = a.reduce((x, y) => x > y ? x : y)     // 求和

对于空数组,不带初始值参数调用 reduce() 将导致类型错误异常。 如果数组只有一个值并没有指定初始值或者一个空数组指定了初始值,reduce() 只是简单的返回那个值,并不会调用化简函数。

  • indexOf() 和 lastIndexOf()

搜索整个数组中具有给定值的元素,返回找到的第一个元素的索引或者如果没有找到就返回 -1。

10.数组类型

var isArray = Array.isArray || function(o) {
    return typeof o === 'object' && Object.prototype.toString.call(o) === '[object Array]'
}

11.类数组对象

JavaScript 数组的一些特性是其他对象所没有的

  • 当新的元素添加到列表中时,自动更新 length 属性
  • 设置 length 为较小值将截断数组
  • 从 Array.prototype 中继承到一些有用的方式
  • 其类属性为 Array

一种常常完全合理的看法把拥有一个数组 length 属性和对应非负整数属性的对象看做一种类型的数组,我们称为类数组。很多数组算法针对类数组对象工作得很好,就像针对真正的数组一样。

var a = {"0": "a", "1": "b", "2": "c", length: 3}   // 类数组对象
Array.prototype.join.call(a, "+");      // "a+b+c"
Array.prototype.slice.call(a);          // ["a", "b", "c"] 真正数组的副本

12.作为数组的字符串

在 ECMAScript 5 中,字符串的行为类似只读的数组。当需要记住,字符串是不可变值,当把它们作为数组看待时,它们是只读的,如 push()、sort()等数组方法会修改数组,但在字符串是无效的。

var s = 'JavaScript';
Array.prototype.join.call(s, '-');	// "J-a-v-a-S-c-r-i-p-t"