TypeScript 数据类型 - 数组

90 阅读11分钟

数组

创建数组

1. 字面量方式

最常见和简单的方法是使用数组字面量([])。

let arr: number[] = [1, 2, 3];
let strArr: string[] = ["a", "b", "c"];

2. 泛型方式

使用 TypeScript 提供的数组泛型 Array<T>

let arr: Array<number> = [1, 2, 3];
let strArr: Array<string> = ["a", "b", "c"];

3. 使用构造函数

通过 Array 构造函数创建数组。

let arr = new Array<number>(1, 2, 3); // [1, 2, 3]
let emptyArr = new Array<number>(5); // 创建长度为5的空数组

4. 通过 Array.of

使用 Array.of 方法创建数组,它可以避免构造函数中长度和元素的混淆。

let arr = Array.of(1, 2, 3); // [1, 2, 3]
let singleValueArr = Array.of(5); // [5]

5. 通过 Array.from

从类数组或可迭代对象创建数组。

let arr = Array.from("hello"); // ['h', 'e', 'l', 'l', 'o']
let mappedArr = Array.from([1, 2, 3], x => x * 2); // [2, 4, 6]

6. 使用扩展运算符

从已有的数组或可迭代对象创建新数组。

let original = [1, 2, 3];
let copy = [...original]; // [1, 2, 3]

7. 多维数组

创建多维数组。

let matrix: number[][] = [
  [1, 2, 3],
  [4, 5, 6],
];

8. 元组创建数组

元组是一种固定长度的数组,可以混合类型。

let tupleArr: [number, string, boolean][] = [
  [1, "hello", true],
  [2, "world", false],
];

添加元素

1. push 方法(改变原数组)

将一个或多个元素添加到数组的末尾。

let arr: number[] = [1, 2, 3];
arr.push(4); // [1, 2, 3, 4]
arr.push(5, 6); // [1, 2, 3, 4, 5, 6]

2. unshift 方法(改变原数组)

将一个或多个元素添加到数组的开头。

let arr: number[] = [2, 3, 4];
arr.unshift(1); // [1, 2, 3, 4]
arr.unshift(-2, -1, 0); // [-2, -1, 0, 1, 2, 3, 4]

3. 索引赋值(改变原数组)

通过指定索引,将元素插入到数组的特定位置。

let arr: number[] = [1, 2, 4];
arr[2] = 3; // 替换位置 2 的值
arr[3] = 5; // 添加到数组末尾

4. splice 方法(改变原数组)

在指定位置插入元素,支持插入多个元素。

let arr: number[] = [1, 2, 5];
arr.splice(2, 0, 3, 4); // [1, 2, 3, 4, 5],从索引 2 开始插入 3 和 4

5. concat 方法

创建一个新数组,将元素添加到原数组的末尾。

let arr: number[] = [1, 2, 3];
let newArr = arr.concat(4, 5); // [1, 2, 3, 4, 5]

6. 扩展运算符

将新元素合并到数组中,生成新数组。

let arr: number[] = [2, 3];
let newArr = [1, ...arr, 4, 5]; // [1, 2, 3, 4, 5]

7. Array.from

通过生成新数组的方式添加元素。

let arr: number[] = [1, 2, 3];
let newArr = Array.from([...arr, 4]); // [1, 2, 3, 4]

8. fill 初始化(改变原数组)

创建数组时填充值,也可作为添加元素的方式。

let arr = new Array<number>(5).fill(0); // [0, 0, 0, 0, 0]

let arr2: number[] = [1, 2, 3];
arr2.fill(0); // [0, 0, 0]
arr2.fill(9, 1, 2); // [0, 9, 0]

9. set 方法(改变原数组)

设置数组的指定索引,即使索引不连续,用于稀疏数组。

let arr: number[] = [];
arr[5] = 10; // 稀疏数组:[undefined × 5, 10]

10. Object.assign(改变原数组)

合并数组,添加新元素。

let arr: number[] = [1, 2, 3];
let newArr = Object.assign([], arr, { 3: 4 }); // [1, 2, 3, 4]

11. TypedArray(改变原数组)

特殊场景,在处理二进制数据时,向 TypedArray 添加数据。

let arr = new Uint8Array(3);
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;

删除元素

1. pop 方法(改变原数组)

删除数组的最后一个元素,并返回被删除的值。

let arr: number[] = [1, 2, 3];
let lastElement = arr.pop(); // arr: [1, 2], lastElement: 3

2. shift 方法(改变原数组)

删除数组的第一个元素,并返回被删除的值。

let arr: number[] = [1, 2, 3];
let firstElement = arr.shift(); // arr: [2, 3], firstElement: 1

3. splice 方法(改变原数组)

删除指定位置的元素,可指定数量。

let arr: number[] = [1, 2, 3, 4, 5];
let removed = arr.splice(1, 2); // 从索引 1 开始删除 2 个元素
// arr: [1, 4, 5], removed: [2, 3]

4. filter 方法

通过条件筛选元素,返回新数组,原数组不变。

let arr: number[] = [1, 2, 3, 4, 5];
let filtered = arr.filter(x => x !== 3); // [1, 2, 4, 5]

5. slice 方法

通过截取生成新数组,原数组不变。

let arr: number[] = [1, 2, 3, 4, 5];
let sliced = arr.slice(0, 2).concat(arr.slice(3)); // [1, 2, 4, 5]

6. delete 操作符(改变原数组)

删除数组中某个索引的元素,但不会更新数组长度,会留下 undefined

let arr: number[] = [1, 2, 3];
delete arr[1]; // arr: [1, undefined, 3]

7. length 属性截断数组(改变原数组)

通过修改 length 属性来删除末尾的元素。

let arr: number[] = [1, 2, 3, 4, 5];
arr.length = 3; // arr: [1, 2, 3]

8. 清空数组(改变原数组)

清空数组的所有元素。

方法 1: 设置 length0
let arr: number[] = [1, 2, 3];
arr.length = 0; // arr: []
方法 2: 使用 splice
let arr: number[] = [1, 2, 3];
arr.splice(0, arr.length); // arr: []
方法 3: 重新赋值空数组
let arr: number[] = [1, 2, 3];
arr = []; // arr: []

9. 遍历删除

遍历数组并删除符合条件的元素。

let arr: number[] = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
  if (arr[i] === 3) {
    arr.splice(i, 1);
    i--; // 调整索引
  }
} // arr: [1, 2, 4, 5]

10. Set 和扩展运算符

从数组中删除重复元素或指定元素。

let arr: number[] = [1, 2, 3, 2, 4];
let uniqueArr = [...new Set(arr)]; // [1, 2, 3, 4]

访问和查找元素

1. 按索引访问

直接通过索引访问数组中的元素。

let arr: number[] = [10, 20, 30, 40];
console.log(arr[1]); // 20
console.log(arr[-1]); // undefined
console.log(arr[arr.length - 1]); // 40

2. 使用 find 方法

查找第一个符合条件的元素。

let arr: number[] = [10, 20, 30, 40];
let found = arr.find(x => x > 25); // 30

3. 使用 findIndex 方法

查找第一个满足条件的元素索引。

let arr: number[] = [10, 20, 30, 40];
let index = arr.findIndex(x => x > 25); // 2

4. 使用 indexOf 方法

查找元素的第一个匹配索引。如果不存在,返回 -1

let arr: number[] = [10, 20, 30, 20];
let index = arr.indexOf(20); // 1

5. 使用 lastIndexOf 方法

查找元素的最后一个匹配索引。

let arr: number[] = [10, 20, 30, 20];
let lastIndex = arr.lastIndexOf(20); // 3

6 使用 includes 方法

检查数组是否包含某个元素,返回 truefalse

let arr: number[] = [10, 20, 30];
let exists = arr.includes(20); // true
let notExists = arr.includes(40); // false

7. 使用 at 方法

ES2022 新增,支持正负索引访问数组。

let arr: number[] = [10, 20, 30];
let element = arr.at(-1); // 30

遍历元素

1. 使用 for 循环

最基础的遍历方式,按索引逐个访问元素。

let arr: number[] = [10, 20, 30];
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]); // 10, 20, 30
}

2. 使用 for...of

遍历数组中的每个元素(不涉及索引)。

let arr: number[] = [10, 20, 30];
for (let value of arr) {
  console.log(value); // 10, 20, 30
}

3. 使用 for...in

遍历数组的索引(不直接获取值)。

let arr: number[] = [10, 20, 30];
for (let index in arr) {
  console.log(index, arr[index]); // 0 10, 1 20, 2 30
}

4. 使用 forEach

数组自带的迭代方法,可访问每个元素和索引。

let arr: number[] = [10, 20, 30];
arr.forEach((value, index) => {
  console.log(index, value); // 0 10, 1 20, 2 30
});

5. 使用 while 循环

使用手动控制索引的方式遍历数组。

let arr: number[] = [10, 20, 30];
let i = 0;
while (i < arr.length) {
  console.log(arr[i]); // 10, 20, 30
  i++;
}

6. 使用 do...while 循环

至少执行一次遍历,适合需要初次强制运行的情况。

let arr: number[] = [10, 20, 30];
let i = 0;
do {
  console.log(arr[i]); // 10, 20, 30
  i++;
} while (i < arr.length);

7. 使用 map

遍历元素并返回一个新数组,同时可执行操作。

let arr: number[] = [10, 20, 30];
let doubled = arr.map(x => x * 2); // [20, 40, 60]

8. 使用 reduce

通过累加器逐个访问元素,同时可以累积结果。

let arr: number[] = [10, 20, 30];
let sum = arr.reduce((acc, val) => acc + val, 0); // 60

9. 使用 reduceRight

通过累加器从右到左逐个访问元素,同时可以累积结果。

let arr: number[] = [10, 20, 30];
let sum = arr.reduceRight((acc, val) => acc + val, 0); // 60

10. 使用 filter

遍历所有元素,并根据条件筛选出新数组。

let arr: number[] = [10, 20, 30, 40];
let filtered = arr.filter(x => x > 20); // [30, 40]

11. 使用 entries

获取数组的索引和值对。

let arr: number[] = [10, 20, 30];
for (let [index, value] of arr.entries()) {
  console.log(index, value); // 0 10, 1 20, 2 30
}

12. 使用 keys

遍历数组的索引。

let arr: number[] = [10, 20, 30];
for (let key of arr.keys()) {
  console.log(key); // 0, 1, 2
}

13. 使用 values

遍历数组的值(类似 for...of)。

let arr: number[] = [10, 20, 30];
for (let value of arr.values()) {
  console.log(value); // 10, 20, 30
}

14. 使用 at 方法与循环

结合正负索引进行遍历。

let arr: number[] = [10, 20, 30];
for (let i = 0; i < arr.length; i++) {
  console.log(arr.at(i)); // 10, 20, 30
}

15. 使用 flatMap

适合对嵌套数组进行扁平化和遍历。

let arr: number[][] = [[10, 20], [30, 40]];
let flatMapped = arr.flatMap(x => x.map(y => y * 2)); // [20, 40, 60, 80]

其他

1. reverse 方法(改变原数组)

将数组中的元素顺序反转。

let arr: number[] = [1, 2, 3];
arr.reverse(); // [3, 2, 1]

2. sort 方法(改变原数组)

2.1 字符串排序

对数组进行原地排序,默认按照 Unicode 编码进行排序。

let strArr = ['banana', 'apple', 'cherry'];
strArr.sort();
console.log(strArr);  // 输出: ['apple', 'banana', 'cherry']
2.2 数字排序
let numArr = [5, 3, 8, 1, 2];
numArr.sort();
console.log(numArr);  // 输出: [1, 2, 3, 5, 8]
2.3 自定义排序
let numArr = [5, 3, 8, 1, 2];
numArr.sort((a, b) => a - b);  // 升序排序
console.log(numArr);  // 输出: [1, 2, 3, 5, 8]

numArr.sort((a, b) => b - a);  // 降序排序
console.log(numArr);  // 输出: [8, 5, 3, 2, 1]
2.4 数字字符串
let numArr = ["5", "1", "8", "10", "2"];
numArr.sort((a, b) => Number(a) - Number(b)); // 将字符串转换为数字进行比较
console.log(numArr); // 输出: ["1", "2", "5", "8", "10"]

numArr.sort((a, b) => Number(b) - Number(a)); // 降序
console.log(numArr); // 输出: [ '10', '8', '5', '2', '1' ]

3. join 方法

将数组的所有元素连接成一个字符串,使用指定的分隔符。

let arr: string[] = ["a", "b", "c"];
let result = arr.join("-"); // "a-b-c"

4. toString 方法

将数组转换为字符串(与 join 类似,默认以逗号分隔)。

let arr: number[] = [1, 2, 3];
let result = arr.toString(); // "1,2,3"

5. flat 方法

将嵌套数组展开为一维数组,可指定展开深度。

let arr: number[][] = [[1, 2], [3, [4, 5]]];
let flatArr = arr.flat(); // [1, 2, 3, [4, 5]]
let deepFlatArr = arr.flat(2); // [1, 2, 3, 4, 5]

6. isArray 静态方法

判断一个对象是否为数组。

let arr: number[] = [1, 2, 3];
console.log(Array.isArray(arr)); // true
console.log(Array.isArray({})); // false

7. copyWithin 方法(改变原数组)

在数组内部浅复制部分元素到其他位置,覆盖原有元素。

let arr: number[] = [1, 2, 3, 4, 5];
arr.copyWithin(1, 3); // [1, 4, 5, 4, 5]

8. toLocaleString 方法

将数组的所有元素转为字符串,并格式化为本地表示。

let arr: number[] = [1000, 2000];
let result = arr.toLocaleString("en-US"); // "1,000,2,000"

9. Array.prototype[@@iterator] 方法

返回一个数组的默认迭代器。

let arr: number[] = [1, 2, 3];
let iterator = arr[Symbol.iterator]();
console.log(iterator.next().value); // 1

10. 属性 length

length 是数组的一个属性,表示数组中的元素个数。可以用来获取或设置数组的长度。

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

arr.length = 2;           // 修改数组长度
console.log(arr);         // 输出: [1, 2]

11. 深拷贝和浅拷贝

在 TypeScript 中,数组的深拷贝和浅拷贝主要用于确保数据独立性和完整性。以下是关于深拷贝和浅拷贝的方法总结:

浅拷贝

浅拷贝只复制数组的引用,嵌套对象和嵌套数组仍然指向原始数据。

1. 通过扩展运算符(...
let original = [1, 2, 3];
let shallowCopy = [...original]; // [1, 2, 3]
2. 通过 Array.prototype.slice
let original = [1, 2, 3];
let shallowCopy = original.slice(); // [1, 2, 3]
3. 通过 Array.prototype.concat
let original = [1, 2, 3];
let shallowCopy = original.concat(); // [1, 2, 3]
深拷贝

深拷贝会递归复制所有嵌套对象和数组,确保新数组与原始数组完全独立。

1. 通过 JSON.parseJSON.stringify

最常见的深拷贝方式,适合纯数组(没有函数和特殊对象)。

let original = [1, { a: 2 }, [3, 4]];
let deepCopy = JSON.parse(JSON.stringify(original));
// 修改 deepCopy 不影响 original
deepCopy[1].a = 10; 
console.log(original); // [1, { a: 2 }, [3, 4]]
console.log(deepCopy); // [1, { a: 10 }, [3, 4]]
2. 通过递归函数

适合自定义复杂的深拷贝需求。

function deepClone<T>(arr: T[]): T[] {
  return arr.map(item =>
    Array.isArray(item)
      ? deepClone(item) // 如果是数组,递归调用
      : typeof item === 'object' && item !== null
      ? { ...item } // 如果是对象,解构拷贝
      : item // 基本类型直接返回
  );
}

let original = [1, { a: 2 }, [3, 4]];
let deepCopy = deepClone(original);
3. 通过第三方库
使用 Lodash 的 cloneDeep

安装 Lodash:

npm install lodash

使用 cloneDeep 方法:

import { cloneDeep } from "lodash";

let original = [1, { a: 2 }, [3, 4]];
let deepCopy = cloneDeep(original);
浅拷贝与深拷贝的对比
方法类型备注
...浅拷贝简单且高效,适用于浅层数组和基本类型。
slice浅拷贝经典方法,适用于兼容旧版 JavaScript。
concat浅拷贝类似 slice,适合连接操作。
JSON.stringify深拷贝快捷,无法处理函数、循环引用、特殊对象等。
自定义递归深拷贝灵活但需要考虑多种数据结构。
Lodash 的 cloneDeep深拷贝功能强大,支持复杂对象和循环引用。