Skip to content

JavaScript Array数组

数组是 JavaScript 中最重要的数据结构之一,用于存储和操作有序的数据集合。JavaScript 提供了丰富的数组方法,使得数组操作变得非常便捷和强大。在本章节中,我们将深入学习 JavaScript 中的数组及其各种操作方法。

什么是数组

数组是一种特殊类型的对象,用于存储有序的元素集合。数组中的每个元素都有一个数字索引,从 0 开始。

数组的特点

  1. 有序性:元素按照插入顺序排列
  2. 索引访问:通过数字索引访问元素
  3. 动态性:大小可以动态改变
  4. 混合类型:可以存储不同类型的数据
  5. 引用类型:数组是对象,通过引用传递

创建数组

数组字面量

javascript
// 空数组
const emptyArray = [];

// 带元素的数组
const fruits = ["苹果", "香蕉", "橙子"];
const numbers = [1, 2, 3, 4, 5];
const mixed = [1, "hello", true, null, { name: "张三" }];

// 多维数组
const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

console.log(fruits[0]); // "苹果"
console.log(numbers[2]); // 3
console.log(matrix[1][2]); // 6

Array 构造函数

javascript
// 创建空数组
const arr1 = new Array();
const arr2 = new Array(5); // 创建长度为5的空数组

// 创建带元素的数组
const arr3 = new Array(1, 2, 3);
const arr4 = new Array("苹果", "香蕉", "橙子");

console.log(arr2.length); // 5
console.log(arr3); // [1, 2, 3]
console.log(arr4); // ["苹果", "香蕉", "橙子"]

// 注意:只有一个数字参数时创建指定长度的数组
const arr5 = new Array(3);
console.log(arr5); // [empty × 3]
console.log(arr5.length); // 3

// 使用 Array.of() 避免这个问题
const arr6 = Array.of(3);
console.log(arr6); // [3]
console.log(arr6.length); // 1

Array.from()

javascript
// 从类数组对象创建数组
function example() {
  return Array.from(arguments);
}

const argsArray = example(1, 2, 3);
console.log(argsArray); // [1, 2, 3]

// 从字符串创建数组
const strArray = Array.from("hello");
console.log(strArray); // ["h", "e", "l", "l", "o"]

// 从 Set 创建数组
const set = new Set([1, 2, 3, 2, 1]);
const setArray = Array.from(set);
console.log(setArray); // [1, 2, 3]

// 从 Map 创建数组
const map = new Map([["a", 1], ["b", 2]]);
const mapArray = Array.from(map);
console.log(mapArray); // [["a", 1], ["b", 2]]

// 使用映射函数
const doubled = Array.from([1, 2, 3], x => x * 2);
console.log(doubled); // [2, 4, 6]

// 创建指定长度的数组并初始化
const zeros = Array.from({ length: 5 }, () => 0);
console.log(zeros); // [0, 0, 0, 0, 0]

const indices = Array.from({ length: 5 }, (_, index) => index);
console.log(indices); // [0, 1, 2, 3, 4]

数组基本操作

访问和修改元素

javascript
const colors = ["红色", "绿色", "蓝色"];

// 访问元素
console.log(colors[0]); // "红色"
console.log(colors[colors.length - 1]); // "蓝色" (最后一个元素)

// 修改元素
colors[1] = "黄色";
console.log(colors); // ["红色", "黄色", "蓝色"]

// 添加元素
colors[3] = "紫色";
console.log(colors); // ["红色", "黄色", "蓝色", "紫色"]

// 访问不存在的索引
console.log(colors[10]); // undefined

数组长度

javascript
const arr = [1, 2, 3];
console.log(arr.length); // 3

// 修改 length 属性
arr.length = 5;
console.log(arr); // [1, 2, 3, undefined, undefined]

arr.length = 2;
console.log(arr); // [1, 2]

// 清空数组
arr.length = 0;
console.log(arr); // []

数组方法

添加和删除元素

javascript
const fruits = ["苹果", "香蕉"];

// push() - 在末尾添加元素
const newLength = fruits.push("橙子");
console.log(fruits); // ["苹果", "香蕉", "橙子"]
console.log(newLength); // 3

// pop() - 删除末尾元素
const lastFruit = fruits.pop();
console.log(fruits); // ["苹果", "香蕉"]
console.log(lastFruit); // "橙子"

// unshift() - 在开头添加元素
const newLength2 = fruits.unshift("葡萄");
console.log(fruits); // ["葡萄", "苹果", "香蕉"]
console.log(newLength2); // 3

// shift() - 删除开头元素
const firstFruit = fruits.shift();
console.log(fruits); // ["苹果", "香蕉"]
console.log(firstFruit); // "葡萄"

// splice() - 添加/删除元素
const vegetables = ["胡萝卜", "土豆", "洋葱"];

// 在索引1处删除1个元素,然后添加"西红柿"和"黄瓜"
const removed = vegetables.splice(1, 1, "西红柿", "黄瓜");
console.log(vegetables); // ["胡萝卜", "西红柿", "黄瓜", "洋葱"]
console.log(removed); // ["土豆"]

// 只添加元素
vegetables.splice(2, 0, "青椒");
console.log(vegetables); // ["胡萝卜", "西红柿", "青椒", "黄瓜", "洋葱"]

// 只删除元素
const removed2 = vegetables.splice(1, 2);
console.log(vegetables); // ["胡萝卜", "黄瓜", "洋葱"]
console.log(removed2); // ["西红柿", "青椒"]

数组连接和复制

javascript
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// concat() - 连接数组
const combined = arr1.concat(arr2);
console.log(combined); // [1, 2, 3, 4, 5, 6]
console.log(arr1); // [1, 2, 3] (原数组不变)

// 连接多个数组
const arr3 = [7, 8];
const all = arr1.concat(arr2, arr3);
console.log(all); // [1, 2, 3, 4, 5, 6, 7, 8]

// 扩展运算符连接数组
const combined2 = [...arr1, ...arr2];
console.log(combined2); // [1, 2, 3, 4, 5, 6]

// slice() - 提取数组片段(浅拷贝)
const numbers = [1, 2, 3, 4, 5];
const slice1 = numbers.slice(1, 4);
console.log(slice1); // [2, 3, 4]
console.log(numbers); // [1, 2, 3, 4, 5] (原数组不变)

// 复制整个数组
const copy = numbers.slice();
console.log(copy); // [1, 2, 3, 4, 5]

// 负索引
const slice2 = numbers.slice(-3, -1);
console.log(slice2); // [3, 4]

// 扩展运算符复制数组
const copy2 = [...numbers];
console.log(copy2); // [1, 2, 3, 4, 5]

// Array.from() 复制数组
const copy3 = Array.from(numbers);
console.log(copy3); // [1, 2, 3, 4, 5]

数组搜索

javascript
const fruits = ["苹果", "香蕉", "橙子", "苹果", "葡萄"];

// indexOf() - 查找元素第一次出现的索引
console.log(fruits.indexOf("苹果")); // 0
console.log(fruits.indexOf("橙子")); // 2
console.log(fruits.indexOf("西瓜")); // -1 (未找到)

// lastIndexOf() - 查找元素最后一次出现的索引
console.log(fruits.lastIndexOf("苹果")); // 3
console.log(fruits.lastIndexOf("西瓜")); // -1 (未找到)

// includes() - 检查是否包含元素
console.log(fruits.includes("香蕉")); // true
console.log(fruits.includes("西瓜")); // false

// find() - 查找满足条件的第一个元素
const numbers = [1, 5, 10, 15, 20];
const found = numbers.find(num => num > 10);
console.log(found); // 15

const users = [
  { id: 1, name: "张三" },
  { id: 2, name: "李四" },
  { id: 3, name: "王五" }
];

const user = users.find(u => u.id === 2);
console.log(user); // { id: 2, name: "李四" }

// findIndex() - 查找满足条件的第一个元素的索引
const index = numbers.findIndex(num => num > 10);
console.log(index); // 3

// filter() - 过滤满足条件的元素
const filtered = numbers.filter(num => num > 10);
console.log(filtered); // [15, 20]

const adults = users.filter(user => user.id > 1);
console.log(adults); // [{ id: 2, name: "李四" }, { id: 3, name: "王五" }]

数组遍历

javascript
const fruits = ["苹果", "香蕉", "橙子"];

// for 循环
for (let i = 0; i < fruits.length; i++) {
  console.log(`${i}: ${fruits[i]}`);
}

// for...of 循环
for (const fruit of fruits) {
  console.log(fruit);
}

// forEach() 方法
fruits.forEach((fruit, index) => {
  console.log(`${index}: ${fruit}`);
});

// forEach() with thisArg
const obj = {
  prefix: "水果:",
  printFruits(arr) {
    arr.forEach(function(fruit) {
      console.log(this.prefix + fruit); // this 指向 obj
    }, this);
  }
};

obj.printFruits(fruits);

// entries() - 获取键值对
for (const [index, fruit] of fruits.entries()) {
  console.log(`${index}: ${fruit}`);
}

// keys() - 获取索引
for (const index of fruits.keys()) {
  console.log(index);
}

// values() - 获取值
for (const fruit of fruits.values()) {
  console.log(fruit);
}

数组转换

javascript
const numbers = [1, 2, 3, 4, 5];

// map() - 转换每个元素
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

const users = [
  { name: "张三", age: 25 },
  { name: "李四", age: 30 },
  { name: "王五", age: 35 }
];

const names = users.map(user => user.name);
console.log(names); // ["张三", "李四", "王五"]

const userInfos = users.map(user => ({
  ...user,
  isAdult: user.age >= 18
}));
console.log(userInfos);

// flat() - 扁平化数组
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6]

// flatMap() - map + flat(1)
const sentences = ["hello world", "how are you"];
const words = sentences.flatMap(sentence => sentence.split(" "));
console.log(words); // ["hello", "world", "how", "are", "you"]

// join() - 连接数组元素为字符串
const fruits = ["苹果", "香蕉", "橙子"];
console.log(fruits.join()); // "苹果,香蕉,橙子"
console.log(fruits.join(" - ")); // "苹果 - 香蕉 - 橙子"
console.log(fruits.join("")); // "苹果香蕉橙子"

数组排序

javascript
// sort() - 排序数组
const fruits = ["香蕉", "苹果", "橙子"];
fruits.sort();
console.log(fruits); // ["橙子", "苹果", "香蕉"] (按 Unicode 排序)

const numbers = [10, 2, 30, 4];
numbers.sort();
console.log(numbers); // [10, 2, 30, 4] (按字符串排序)

// 数字排序
numbers.sort((a, b) => a - b);
console.log(numbers); // [2, 4, 10, 30] (升序)

numbers.sort((a, b) => b - a);
console.log(numbers); // [30, 10, 4, 2] (降序)

// 对象排序
const users = [
  { name: "张三", age: 25 },
  { name: "李四", age: 30 },
  { name: "王五", age: 20 }
];

// 按年龄排序
users.sort((a, b) => a.age - b.age);
console.log(users);

// 按姓名排序
users.sort((a, b) => a.name.localeCompare(b.name));
console.log(users);

// reverse() - 反转数组
const arr = [1, 2, 3, 4, 5];
arr.reverse();
console.log(arr); // [5, 4, 3, 2, 1]

数组聚合

javascript
const numbers = [1, 2, 3, 4, 5];

// reduce() - 累积计算
const sum = numbers.reduce((accumulator, current) => accumulator + current, 0);
console.log(sum); // 15

// 计算平均值
const average = numbers.reduce((acc, curr, index, array) => {
  acc += curr;
  if (index === array.length - 1) {
    return acc / array.length;
  }
  return acc;
}, 0);
console.log(average); // 3

// 查找最大值
const max = numbers.reduce((max, current) => Math.max(max, current));
console.log(max); // 5

// 统计元素出现次数
const fruits = ["苹果", "香蕉", "苹果", "橙子", "香蕉", "苹果"];
const count = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});
console.log(count); // { 苹果: 3, 香蕉: 2, 橙子: 1 }

// reduceRight() - 从右到左累积
const result = numbers.reduceRight((acc, curr) => acc + curr, 0);
console.log(result); // 15

// every() - 检查所有元素是否满足条件
const allPositive = numbers.every(num => num > 0);
console.log(allPositive); // true

const hasNegative = numbers.every(num => num < 0);
console.log(hasNegative); // false

// some() - 检查是否有元素满足条件
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true

const allEven = numbers.some(num => num % 2 !== 0);
console.log(allEven); // true

数组的现代方法

ES2022 新方法

javascript
const numbers = [1, 2, 3, 4, 5];

// at() - 支持负索引访问
console.log(numbers.at(0)); // 1
console.log(numbers.at(-1)); // 5 (最后一个元素)
console.log(numbers.at(-2)); // 4 (倒数第二个元素)

// findLast() - 从后往前查找
const lastEven = numbers.findLast(num => num % 2 === 0);
console.log(lastEven); // 4

// findLastIndex() - 从后往前查找索引
const lastEvenIndex = numbers.findLastIndex(num => num % 2 === 0);
console.log(lastEvenIndex); // 3

数组分组

javascript
// groupBy (提案阶段,需要 polyfill)
// const users = [
//   { name: "张三", department: "IT" },
//   { name: "李四", department: "HR" },
//   { name: "王五", department: "IT" }
// ];

// const grouped = users.group(user => user.department);
// console.log(grouped);

// 手动实现分组
function groupBy(array, keyFunction) {
  return array.reduce((groups, item) => {
    const key = keyFunction(item);
    if (!groups[key]) {
      groups[key] = [];
    }
    groups[key].push(item);
    return groups;
  }, {});
}

const users = [
  { name: "张三", department: "IT" },
  { name: "李四", department: "HR" },
  { name: "王五", department: "IT" }
];

const grouped = groupBy(users, user => user.department);
console.log(grouped);

数组最佳实践

1. 避免稀疏数组

javascript
// 不好的做法
const sparse = new Array(3);
sparse[0] = "第一个";
sparse[2] = "第三个";
console.log(sparse); // [ "第一个", empty, "第三个" ]

// 好的做法
const dense = Array.from({ length: 3 }, () => undefined);
dense[0] = "第一个";
dense[2] = "第三个";
console.log(dense); // [ "第一个", undefined, "第三个" ]

// 或者直接初始化
const arr = ["第一个", undefined, "第三个"];
console.log(arr); // [ "第一个", undefined, "第三个" ]

2. 使用函数式方法

javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 不好的做法 - 使用 for 循环
function getEvenSquares(numbers) {
  const result = [];
  for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
      result.push(numbers[i] * numbers[i]);
    }
  }
  return result;
}

// 好的做法 - 使用函数式方法
const evenSquares = numbers
  .filter(num => num % 2 === 0)
  .map(num => num * num);

console.log(evenSquares); // [4, 16, 36, 64, 100]

3. 链式调用

javascript
const products = [
  { name: "笔记本电脑", price: 5000, category: "电子" },
  { name: "手机", price: 3000, category: "电子" },
  { name: "书籍", price: 50, category: "图书" },
  { name: "耳机", price: 200, category: "电子" }
];

// 链式调用处理数据
const result = products
  .filter(product => product.category === "电子")
  .filter(product => product.price > 1000)
  .map(product => ({
    ...product,
    discountPrice: product.price * 0.9
  }))
  .sort((a, b) => a.discountPrice - b.discountPrice)
  .map(product => product.name);

console.log(result); // ["耳机", "手机", "笔记本电脑"]

4. 性能优化

javascript
const largeArray = Array.from({ length: 1000000 }, (_, i) => i);

// 对于大数组,避免多次遍历
// 不好的做法
// const result = largeArray
//   .filter(x => x % 2 === 0)
//   .map(x => x * 2)
//   .filter(x => x > 1000);

// 好的做法 - 一次遍历
const result = largeArray.reduce((acc, x) => {
  if (x % 2 === 0) {
    const doubled = x * 2;
    if (doubled > 1000) {
      acc.push(doubled);
    }
  }
  return acc;
}, []);

// 对于简单操作,传统循环可能更快
function doubleEvenNumbers(arr) {
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] % 2 === 0) {
      result.push(arr[i] * 2);
    }
  }
  return result;
}

数组应用示例

1. 数据处理管道

javascript
class DataPipeline {
  constructor(data) {
    this.data = Array.isArray(data) ? data : [data];
  }
  
  // 过滤数据
  filter(predicate) {
    this.data = this.data.filter(predicate);
    return this;
  }
  
  // 转换数据
  map(transformer) {
    this.data = this.data.map(transformer);
    return this;
  }
  
  // 排序数据
  sort(comparator) {
    this.data = this.data.sort(comparator);
    return this;
  }
  
  // 限制数量
  limit(count) {
    this.data = this.data.slice(0, count);
    return this;
  }
  
  // 跳过数量
  skip(count) {
    this.data = this.data.slice(count);
    return this;
  }
  
  // 扁平化
  flat(depth = 1) {
    this.data = this.data.flat(depth);
    return this;
  }
  
  // 去重
  unique(keyFunction) {
    if (keyFunction) {
      const seen = new Set();
      this.data = this.data.filter(item => {
        const key = keyFunction(item);
        if (seen.has(key)) {
          return false;
        }
        seen.add(key);
        return true;
      });
    } else {
      this.data = [...new Set(this.data)];
    }
    return this;
  }
  
  // 分组
  groupBy(keyFunction) {
    this.data = this.data.reduce((groups, item) => {
      const key = keyFunction(item);
      if (!groups[key]) {
        groups[key] = [];
      }
      groups[key].push(item);
      return groups;
    }, {});
    return this;
  }
  
  // 获取结果
  toArray() {
    return this.data;
  }
  
  // 获取第一个元素
  first() {
    return this.data[0];
  }
  
  // 获取最后一个元素
  last() {
    return this.data[this.data.length - 1];
  }
  
  // 统计
  count() {
    return this.data.length;
  }
  
  // 检查是否为空
  isEmpty() {
    return this.data.length === 0;
  }
}

// 使用示例
const users = [
  { name: "张三", age: 25, department: "IT", salary: 8000 },
  { name: "李四", age: 30, department: "HR", salary: 6000 },
  { name: "王五", age: 35, department: "IT", salary: 12000 },
  { name: "赵六", age: 28, department: "IT", salary: 9000 },
  { name: "钱七", age: 32, department: "HR", salary: 7000 }
];

// 构建数据处理管道
const result = new DataPipeline(users)
  .filter(user => user.department === "IT")
  .filter(user => user.salary > 8000)
  .sort((a, b) => b.salary - a.salary)
  .limit(2)
  .map(user => ({
    name: user.name,
    salary: user.salary,
    annualSalary: user.salary * 12
  }))
  .toArray();

console.log(result);
// [
//   { name: "王五", salary: 12000, annualSalary: 144000 },
//   { name: "赵六", salary: 9000, annualSalary: 108000 }
// ]

// 其他操作
const pipeline = new DataPipeline([1, 2, 2, 3, 3, 4, 5]);
console.log(pipeline.unique().toArray()); // [1, 2, 3, 4, 5]
console.log(pipeline.count()); // 5
console.log(pipeline.first()); // 1
console.log(pipeline.last()); // 5

2. 数组工具类

javascript
class ArrayUtils {
  // 数组分块
  static chunk(array, size) {
    const chunks = [];
    for (let i = 0; i < array.length; i += size) {
      chunks.push(array.slice(i, i + size));
    }
    return chunks;
  }
  
  // 数组差集
  static difference(array, ...values) {
    const flatValues = values.flat();
    return array.filter(item => !flatValues.includes(item));
  }
  
  // 数组交集
  static intersection(array, ...values) {
    const flatValues = values.flat();
    return array.filter(item => flatValues.includes(item));
  }
  
  // 数组并集
  static union(array, ...values) {
    const flatValues = values.flat();
    return [...new Set([...array, ...flatValues])];
  }
  
  // 数组去重(支持对象)
  static unique(array, keyFunction) {
    if (keyFunction) {
      const seen = new Set();
      return array.filter(item => {
        const key = keyFunction(item);
        if (seen.has(key)) {
          return false;
        }
        seen.add(key);
        return true;
      });
    }
    return [...new Set(array)];
  }
  
  // 数组随机排序
  static shuffle(array) {
    const result = [...array];
    for (let i = result.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [result[i], result[j]] = [result[j], result[i]];
    }
    return result;
  }
  
  // 获取数组中的最大值
  static max(array, keyFunction) {
    if (array.length === 0) return undefined;
    
    if (keyFunction) {
      return array.reduce((max, current) => 
        keyFunction(current) > keyFunction(max) ? current : max
      );
    }
    
    return Math.max(...array);
  }
  
  // 获取数组中的最小值
  static min(array, keyFunction) {
    if (array.length === 0) return undefined;
    
    if (keyFunction) {
      return array.reduce((min, current) => 
        keyFunction(current) < keyFunction(min) ? current : min
      );
    }
    
    return Math.min(...array);
  }
  
  // 数组求和
  static sum(array, keyFunction) {
    if (keyFunction) {
      return array.reduce((sum, item) => sum + keyFunction(item), 0);
    }
    return array.reduce((sum, num) => sum + num, 0);
  }
  
  // 数组平均值
  static average(array, keyFunction) {
    if (array.length === 0) return 0;
    
    const sum = this.sum(array, keyFunction);
    return sum / array.length;
  }
  
  // 数组范围
  static range(start, end, step = 1) {
    const result = [];
    if (step > 0) {
      for (let i = start; i < end; i += step) {
        result.push(i);
      }
    } else if (step < 0) {
      for (let i = start; i > end; i += step) {
        result.push(i);
      }
    }
    return result;
  }
  
  // 数组填充
  static fill(array, value, start = 0, end = array.length) {
    const result = [...array];
    for (let i = start; i < end; i++) {
      result[i] = value;
    }
    return result;
  }
  
  // 数组压缩
  static zip(...arrays) {
    const minLength = Math.min(...arrays.map(arr => arr.length));
    const result = [];
    
    for (let i = 0; i < minLength; i++) {
      result.push(arrays.map(arr => arr[i]));
    }
    
    return result;
  }
}

// 使用示例
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 分块
console.log(ArrayUtils.chunk(numbers, 3)); 
// [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

// 差集
console.log(ArrayUtils.difference([1, 2, 3, 4, 5], [2, 4])); // [1, 3, 5]

// 交集
console.log(ArrayUtils.intersection([1, 2, 3], [2, 3, 4], [3, 4, 5])); // [3]

// 并集
console.log(ArrayUtils.union([1, 2], [2, 3], [3, 4])); // [1, 2, 3, 4]

// 去重(对象)
const users = [
  { id: 1, name: "张三" },
  { id: 2, name: "李四" },
  { id: 1, name: "张三" }
];
console.log(ArrayUtils.unique(users, user => user.id));

// 随机排序
console.log(ArrayUtils.shuffle([1, 2, 3, 4, 5]));

// 最大值和最小值
console.log(ArrayUtils.max([1, 5, 3, 9, 2])); // 9
console.log(ArrayUtils.min(users, user => user.id)); // { id: 1, name: "张三" }

// 求和和平均值
console.log(ArrayUtils.sum([1, 2, 3, 4, 5])); // 15
console.log(ArrayUtils.average(users, user => user.id)); // 1.5

// 范围
console.log(ArrayUtils.range(0, 10, 2)); // [0, 2, 4, 6, 8]

// 填充
console.log(ArrayUtils.fill([1, 2, 3, 4, 5], 0, 1, 4)); // [1, 0, 0, 0, 5]

// 压缩
console.log(ArrayUtils.zip([1, 2, 3], ["a", "b", "c"], [true, false, true]));
// [[1, "a", true], [2, "b", false], [3, "c", true]]

3. 观察者模式数组

javascript
class ObservableArray extends Array {
  constructor(...items) {
    super(...items);
    this.observers = [];
  }
  
  // 添加观察者
  addObserver(observer) {
    this.observers.push(observer);
  }
  
  // 移除观察者
  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }
  
  // 通知观察者
  notifyObservers(method, ...args) {
    this.observers.forEach(observer => {
      if (typeof observer[method] === "function") {
        observer[method](this, ...args);
      }
    });
  }
  
  // 重写数组方法
  push(...items) {
    const result = super.push(...items);
    this.notifyObservers("onPush", items);
    return result;
  }
  
  pop() {
    const result = super.pop();
    this.notifyObservers("onPop", result);
    return result;
  }
  
  shift() {
    const result = super.shift();
    this.notifyObservers("onShift", result);
    return result;
  }
  
  unshift(...items) {
    const result = super.unshift(...items);
    this.notifyObservers("onUnshift", items);
    return result;
  }
  
  splice(start, deleteCount, ...items) {
    const result = super.splice(start, deleteCount, ...items);
    this.notifyObservers("onSplice", start, deleteCount, items, result);
    return result;
  }
  
  // 其他方法...
}

// 观察者示例
class ArrayLogger {
  onPush(array, items) {
    console.log(`添加了 ${items.length} 个项目:`, items);
  }
  
  onPop(array, item) {
    console.log(`移除了项目:`, item);
  }
  
  onShift(array, item) {
    console.log(`从开头移除了项目:`, item);
  }
  
  onUnshift(array, items) {
    console.log(`在开头添加了 ${items.length} 个项目:`, items);
  }
  
  onSplice(array, start, deleteCount, items, removed) {
    console.log(`在索引 ${start} 处删除了 ${deleteCount} 个项目,添加了 ${items.length} 个项目`);
    if (removed.length > 0) {
      console.log(`删除的项目:`, removed);
    }
    if (items.length > 0) {
      console.log(`添加的项目:`, items);
    }
  }
}

// 使用示例
const observableArray = new ObservableArray(1, 2, 3);
const logger = new ArrayLogger();

observableArray.addObserver(logger);

observableArray.push(4, 5); // 添加了 2 个项目: [4, 5]
observableArray.pop(); // 移除了项目: 5
observableArray.unshift(0); // 在开头添加了 1 个项目: [0]
observableArray.shift(); // 从开头移除了项目: 0
observableArray.splice(1, 1, 10, 20); // 在索引 1 处删除了 1 个项目,添加了 2 个项目

总结

JavaScript 数组的核心要点:

  1. 创建方式:字面量、构造函数、Array.from()、Array.of()
  2. 基本操作:访问、修改、添加、删除元素
  3. 遍历方法:forEach()、for...of、entries()、keys()、values()
  4. 转换方法:map()、filter()、reduce()、flat()、flatMap()
  5. 搜索方法:find()、findIndex()、indexOf()、includes()
  6. 排序方法:sort()、reverse()
  7. 连接方法:concat()、slice()、join()
  8. 现代方法:at()、findLast()、findLastIndex()
  9. 最佳实践:避免稀疏数组、使用函数式方法、性能优化
  10. 实际应用:数据处理管道、工具类、观察者模式

数组是 JavaScript 编程的基础,掌握数组的各种方法和技巧对于提高编程效率至关重要。在下一章节中,我们将学习 JavaScript 的 Number 对象。

本站内容仅供学习和研究使用。