当前位置: 首页 > news >正文

JavaScript学习教程,从入门到精通,JavaScript 数组与引用类型语法知识点及案例代码(10)

JavaScript 数组与引用类型语法知识点及案例代码

一、认识引用类型

在JavaScript中,数据类型分为基本类型和引用类型。数组属于引用类型,这意味着:

  • 基本类型(Number, String, Boolean, Null, Undefined, Symbol)直接存储值
  • 引用类型(Array, Object, Function)存储的是内存地址
// 基本类型示例
let a = 10;
let b = a;  // b获取的是a的值的一个副本
a = 20;
console.log(b); // 10,b不受a后续变化的影响

// 引用类型示例
let arr1 = [1, 2, 3];
let arr2 = arr1;  // arr2和arr1指向同一个内存地址
arr1.push(4);
console.log(arr2); // [1, 2, 3, 4],arr2会随着arr1的变化而变化

二、数组基础

1. 什么是数组

数组是用于存储多个值的单一对象,每个值称为一个元素,元素可以是任意数据类型。

2. 定义数组

// 1. 数组字面量(推荐)
let fruits = ['Apple', 'Banana', 'Orange'];

// 2. Array构造函数
let numbers = new Array(1, 2, 3);

// 3. 指定长度的空数组
let emptyArray = new Array(5); // 创建长度为5的空数组

console.log(fruits); // ["Apple", "Banana", "Orange"]
console.log(numbers); // [1, 2, 3]
console.log(emptyArray); // [empty × 5]

三、数组元素操作

1. 访问元素

let colors = ['red', 'green', 'blue'];

// 通过索引访问(从0开始)
console.log(colors[0]); // "red"
console.log(colors[2]); // "blue"

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

2. 修改元素

let colors = ['red', 'green', 'blue'];

// 修改指定索引的元素
colors[1] = 'yellow';
console.log(colors); // ["red", "yellow", "blue"]

// 添加新元素到数组末尾
colors[3] = 'purple';
console.log(colors); // ["red", "yellow", "blue", "purple"]

3. 数组长度

let languages = ['JavaScript', 'Python', 'Java'];

// 获取数组长度
console.log(languages.length); // 3

// 修改length属性可以截断或扩展数组
languages.length = 2;
console.log(languages); // ["JavaScript", "Python"]

languages.length = 4;
console.log(languages); // ["JavaScript", "Python", empty × 2]

四、数组遍历

1. for循环

let numbers = [10, 20, 30, 40];

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

2. for…of循环

let fruits = ['Apple', 'Banana', 'Orange'];

// for...of循环(直接获取元素值)
for (let fruit of fruits) {
    console.log(fruit);
}

3. forEach方法

let colors = ['red', 'green', 'blue'];

// forEach方法
colors.forEach(function(color, index) {
    console.log(`Color at index ${index} is ${color}`);
});

五、数组元素定位

1. indexOf / lastIndexOf

let numbers = [1, 2, 3, 4, 3, 5];

// 查找元素第一次出现的索引
console.log(numbers.indexOf(3)); // 2

// 查找元素最后一次出现的索引
console.log(numbers.lastIndexOf(3)); // 4

// 找不到返回-1
console.log(numbers.indexOf(10)); // -1

2. find / findIndex

let users = [
    {id: 1, name: 'John'},
    {id: 2, name: 'Jane'},
    {id: 3, name: 'Bob'}
];

// find返回第一个满足条件的元素
let user = users.find(item => item.id === 2);
console.log(user); // {id: 2, name: "Jane"}

// findIndex返回第一个满足条件的元素的索引
let index = users.findIndex(item => item.name === 'Bob');
console.log(index); // 2

3. includes

let fruits = ['Apple', 'Banana', 'Orange'];

// 检查数组是否包含某个元素
console.log(fruits.includes('Banana')); // true
console.log(fruits.includes('Grape')); // false

六、数组排序

1. sort方法

let numbers = [3, 1, 4, 2, 5];

// 默认排序(按字符串Unicode码点)
numbers.sort();
console.log(numbers); // [1, 2, 3, 4, 5]

// 自定义排序
let scores = [95, 70, 88, 60, 100];
scores.sort((a, b) => a - b); // 升序
console.log(scores); // [60, 70, 88, 95, 100]

scores.sort((a, b) => b - a); // 降序
console.log(scores); // [100, 95, 88, 70, 60]

2. reverse方法

let letters = ['a', 'b', 'c', 'd'];

// 反转数组
letters.reverse();
console.log(letters); // ["d", "c", "b", "a"]

七、数组相关方法

1. 添加/删除元素

let fruits = ['Apple', 'Banana'];

// push - 末尾添加
fruits.push('Orange');
console.log(fruits); // ["Apple", "Banana", "Orange"]

// pop - 末尾删除
let last = fruits.pop();
console.log(last); // "Orange"
console.log(fruits); // ["Apple", "Banana"]

// unshift - 开头添加
fruits.unshift('Grape');
console.log(fruits); // ["Grape", "Apple", "Banana"]

// shift - 开头删除
let first = fruits.shift();
console.log(first); // "Grape"
console.log(fruits); // ["Apple", "Banana"]

2. splice方法

let numbers = [1, 2, 3, 4, 5];

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

// 添加元素
numbers.splice(1, 0, 'a', 'b'); // 从索引1开始删除0个元素,添加'a','b'
console.log(numbers); // [1, "a", "b", 4, 5]

// 替换元素
numbers.splice(2, 1, 'c'); // 从索引2开始删除1个元素,添加'c'
console.log(numbers); // [1, "a", "c", 4, 5]

3. slice方法

let colors = ['red', 'green', 'blue', 'yellow', 'purple'];

// 截取数组
let subColors = colors.slice(1, 4); // 从索引1到索引4(不包括4)
console.log(subColors); // ["green", "blue", "yellow"]

// 复制数组
let copy = colors.slice();
console.log(copy); // ["red", "green", "blue", "yellow", "purple"]

4. concat方法

let arr1 = [1, 2];
let arr2 = [3, 4];

// 合并数组
let combined = arr1.concat(arr2);
console.log(combined); // [1, 2, 3, 4]

// 合并多个数组或值
let arr3 = [5, 6];
let all = arr1.concat(arr2, arr3, 7);
console.log(all); // [1, 2, 3, 4, 5, 6, 7]

5. join方法

let names = ['John', 'Jane', 'Bob'];

// 数组转字符串
let str = names.join(', ');
console.log(str); // "John, Jane, Bob"

// 空join
console.log(names.join()); // "John,Jane,Bob"
console.log(names.join('')); // "JohnJaneBob"

6. map方法

let numbers = [1, 2, 3, 4];

// 对每个元素执行函数并返回新数组
let squares = numbers.map(num => num * num);
console.log(squares); // [1, 4, 9, 16]

// 处理对象数组
let users = [
    {name: 'John', age: 25},
    {name: 'Jane', age: 30}
];
let names = users.map(user => user.name);
console.log(names); // ["John", "Jane"]

7. filter方法

let numbers = [1, 2, 3, 4, 5, 6];

// 过滤满足条件的元素
let evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6]

// 过滤对象数组
let products = [
    {name: 'Apple', price: 1.5},
    {name: 'Banana', price: 0.5},
    {name: 'Orange', price: 2}
];
let cheap = products.filter(product => product.price < 1);
console.log(cheap); // [{name: "Banana", price: 0.5}]

8. reduce方法

let numbers = [1, 2, 3, 4];

// 累加
let sum = numbers.reduce((accumulator, current) => accumulator + current, 0);
console.log(sum); // 10

// 计算最大值
let max = numbers.reduce((a, b) => Math.max(a, b));
console.log(max); // 4

// 复杂示例 - 统计字母出现次数
let words = ['hello', 'world', 'javascript'];
let letterCount = words.reduce((acc, word) => {
    for (let letter of word) {
        acc[letter] = (acc[letter] || 0) + 1;
    }
    return acc;
}, {});
console.log(letterCount); // {h: 2, e: 1, l: 4, o: 3, w: 1, r: 2, d: 1, j: 1, a: 2, v: 1, s: 1, c: 1, i: 1, p: 1, t: 1}

八、【示例】奇偶数组

/**
 * 奇偶数组分割示例
 * 将一个数组分割为奇数数组和偶数数组
 */

function splitOddEven(arr) {
    // 使用filter方法筛选奇数
    let odd = arr.filter(num => num % 2 !== 0);
    
    // 使用filter方法筛选偶数
    let even = arr.filter(num => num % 2 === 0);
    
    return { odd, even };
}

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result = splitOddEven(numbers);

console.log('原始数组:', numbers);
console.log('奇数数组:', result.odd); // [1, 3, 5, 7, 9]
console.log('偶数数组:', result.even); // [2, 4, 6, 8, 10]

九、综合案例:地区选择器

/**
 * 地区选择器
 * 实现省市区三级联动选择功能
 */

// 模拟地区数据
const areaData = {
    '北京市': {
        '北京市': ['东城区', '西城区', '朝阳区', '丰台区', '石景山区', '海淀区']
    },
    '广东省': {
        '广州市': ['荔湾区', '越秀区', '海珠区', '天河区', '白云区'],
        '深圳市': ['罗湖区', '福田区', '南山区', '宝安区', '龙岗区']
    },
    '浙江省': {
        '杭州市': ['上城区', '下城区', '江干区', '拱墅区', '西湖区'],
        '宁波市': ['海曙区', '江北区', '北仑区', '镇海区', '鄞州区']
    }
};

// 获取DOM元素
const provinceSelect = document.getElementById('province');
const citySelect = document.getElementById('city');
const districtSelect = document.getElementById('district');

/**
 * 初始化省份选择框
 */
function initProvince() {
    // 获取所有省份
    const provinces = Object.keys(areaData);
    
    // 清空并添加默认选项
    provinceSelect.innerHTML = '<option value="">请选择省份</option>';
    
    // 添加省份选项
    provinces.forEach(province => {
        const option = document.createElement('option');
        option.value = province;
        option.textContent = province;
        provinceSelect.appendChild(option);
    });
    
    // 添加事件监听
    provinceSelect.addEventListener('change', updateCities);
}

/**
 * 更新城市选择框
 */
function updateCities() {
    // 获取选中的省份
    const province = provinceSelect.value;
    
    // 清空并添加默认选项
    citySelect.innerHTML = '<option value="">请选择城市</option>';
    districtSelect.innerHTML = '<option value="">请选择区县</option>';
    
    if (!province) return;
    
    // 获取该省份下的城市
    const cities = Object.keys(areaData[province]);
    
    // 添加城市选项
    cities.forEach(city => {
        const option = document.createElement('option');
        option.value = city;
        option.textContent = city;
        citySelect.appendChild(option);
    });
    
    // 添加事件监听
    citySelect.addEventListener('change', updateDistricts);
}

/**
 * 更新区县选择框
 */
function updateDistricts() {
    // 获取选中的省份和城市
    const province = provinceSelect.value;
    const city = citySelect.value;
    
    // 清空并添加默认选项
    districtSelect.innerHTML = '<option value="">请选择区县</option>';
    
    if (!province || !city) return;
    
    // 获取该城市下的区县
    const districts = areaData[province][city];
    
    // 添加区县选项
    districts.forEach(district => {
        const option = document.createElement('option');
        option.value = district;
        option.textContent = district;
        districtSelect.appendChild(option);
    });
}

// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', initProvince);

// HTML结构示例(实际使用时需要添加到页面中):
/*
<div>
    <select id="province">
        <option value="">请选择省份</option>
    </select>
    <select id="city">
        <option value="">请选择城市</option>
    </select>
    <select id="district">
        <option value="">请选择区县</option>
    </select>
</div>
*/

十、其他实用数组方法

1. some / every

let numbers = [1, 2, 3, 4, 5];

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

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

2. flat / flatMap

let nestedArray = [1, [2, 3], [4, [5, 6]]];

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

// flatMap - 先映射后扁平化
let phrases = ["hello world", "the quick brown fox"];
let words = phrases.flatMap(phrase => phrase.split(" "));
console.log(words); // ["hello", "world", "the", "quick", "brown", "fox"]

3. fill方法

// 创建一个长度为5的数组并用0填充
let zeros = new Array(5).fill(0);
console.log(zeros); // [0, 0, 0, 0, 0]

// 填充部分元素
let numbers = [1, 2, 3, 4, 5];
numbers.fill(0, 1, 3); // 从索引1到3(不包括3)填充0
console.log(numbers); // [1, 0, 0, 4, 5]

4. Array.from

// 从类数组对象创建数组
let arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};
let arr = Array.from(arrayLike);
console.log(arr); // ["a", "b", "c"]

// 从可迭代对象创建数组
let set = new Set(['x', 'y', 'z']);
let arrFromSet = Array.from(set);
console.log(arrFromSet); // ["x", "y", "z"]

// 带映射函数的Array.from
let squares = Array.from([1, 2, 3], x => x * x);
console.log(squares); // [1, 4, 9]

以上内容涵盖了JavaScript数组的主要知识点和实用案例,包括数组的定义、操作、遍历、排序和各种常用方法,以及两个综合案例。这些知识点是JavaScript开发中非常基础和重要的部分。

案例代码

案例1:购物车功能实现

/**
 * 购物车功能实现
 * 包含添加商品、删除商品、计算总价、清空购物车等功能
 */

class ShoppingCart {
  constructor() {
    this.items = []; // 存储购物车商品
  }

  // 添加商品
  addItem(product, quantity = 1) {
    // 检查商品是否已存在
    const existingItem = this.items.find(item => item.id === product.id);
    
    if (existingItem) {
      // 商品已存在,增加数量
      existingItem.quantity += quantity;
    } else {
      // 商品不存在,添加新商品
      this.items.push({
        id: product.id,
        name: product.name,
        price: product.price,
        quantity: quantity
      });
    }
    console.log(`已添加 ${quantity}${product.name} 到购物车`);
  }

  // 删除商品
  removeItem(productId, quantity = 1) {
    const itemIndex = this.items.findIndex(item => item.id === productId);
    
    if (itemIndex === -1) {
      console.log('购物车中未找到该商品');
      return;
    }
    
    const item = this.items[itemIndex];
    
    if (item.quantity <= quantity) {
      // 删除整个商品项
      this.items.splice(itemIndex, 1);
      console.log(`已从购物车移除 ${item.name}`);
    } else {
      // 减少商品数量
      item.quantity -= quantity;
      console.log(`已从购物车减少 ${quantity}${item.name}`);
    }
  }

  // 计算总价
  calculateTotal() {
    return this.items.reduce((total, item) => {
      return total + (item.price * item.quantity);
    }, 0).toFixed(2);
  }

  // 清空购物车
  clearCart() {
    this.items = [];
    console.log('购物车已清空');
  }

  // 显示购物车内容
  displayCart() {
    console.log('购物车内容:');
    this.items.forEach(item => {
      console.log(`${item.name} x ${item.quantity} - ¥${(item.price * item.quantity).toFixed(2)}`);
    });
    console.log(`总计: ¥${this.calculateTotal()}`);
  }
}

// 使用示例
const cart = new ShoppingCart();

// 商品数据
const products = [
  { id: 1, name: 'iPhone 13', price: 5999 },
  { id: 2, name: 'AirPods Pro', price: 1499 },
  { id: 3, name: 'MacBook Pro', price: 12999 }
];

// 添加商品
cart.addItem(products[0]); // 添加1件iPhone
cart.addItem(products[1], 2); // 添加2件AirPods
cart.addItem(products[0]); // 再次添加iPhone

// 显示购物车
cart.displayCart();

// 删除商品
cart.removeItem(1); // 移除1件iPhone
cart.removeItem(2, 1); // 移除1件AirPods

// 显示更新后的购物车
cart.displayCart();

// 清空购物车
cart.clearCart();
cart.displayCart();

案例2:任务管理系统

/**
 * 任务管理系统
 * 包含添加任务、完成任务、删除任务、筛选任务等功能
 */

class TaskManager {
  constructor() {
    this.tasks = []; // 存储所有任务
    this.filters = {
      status: 'all', // all, active, completed
      priority: 'all' // all, high, medium, low
    };
  }

  // 添加任务
  addTask(title, priority = 'medium') {
    const newTask = {
      id: Date.now(), // 使用时间戳作为唯一ID
      title,
      priority,
      completed: false,
      createdAt: new Date()
    };
    
    this.tasks.push(newTask);
    console.log(`任务 "${title}" 已添加`);
    this.displayTasks();
  }

  // 完成任务
  completeTask(taskId) {
    const task = this.tasks.find(t => t.id === taskId);
    if (task) {
      task.completed = true;
      console.log(`任务 "${task.title}" 已完成`);
      this.displayTasks();
    } else {
      console.log('未找到该任务');
    }
  }

  // 删除任务
  deleteTask(taskId) {
    const index = this.tasks.findIndex(t => t.id === taskId);
    if (index !== -1) {
      const deletedTask = this.tasks.splice(index, 1)[0];
      console.log(`任务 "${deletedTask.title}" 已删除`);
      this.displayTasks();
    } else {
      console.log('未找到该任务');
    }
  }

  // 设置筛选条件
  setFilter(type, value) {
    if (this.filters.hasOwnProperty(type)) {
      this.filters[type] = value;
      console.log(`筛选条件已更新: ${type}=${value}`);
      this.displayTasks();
    } else {
      console.log('无效的筛选类型');
    }
  }

  // 获取筛选后的任务
  getFilteredTasks() {
    return this.tasks.filter(task => {
      // 状态筛选
      const statusMatch = 
        this.filters.status === 'all' ||
        (this.filters.status === 'active' && !task.completed) ||
        (this.filters.status === 'completed' && task.completed);
      
      // 优先级筛选
      const priorityMatch = 
        this.filters.priority === 'all' || 
        task.priority === this.filters.priority;
      
      return statusMatch && priorityMatch;
    });
  }

  // 显示任务
  displayTasks() {
    const filteredTasks = this.getFilteredTasks();
    
    console.log('\n当前任务列表:');
    console.log(`状态: ${this.filters.status} | 优先级: ${this.filters.priority}`);
    console.log('--------------------------------');
    
    if (filteredTasks.length === 0) {
      console.log('没有任务');
      return;
    }
    
    filteredTasks.forEach(task => {
      const status = task.completed ? '✓' : ' ';
      const priorityColors = {
        high: '\x1b[31m', // 红色
        medium: '\x1b[33m', // 黄色
        low: '\x1b[32m' // 绿色
      };
      const priorityText = `${priorityColors[task.priority]}${task.priority}\x1b[0m`;
      
      console.log(`[${status}] ${task.id} - ${task.title} (优先级: ${priorityText})`);
    });
    
    console.log(`${filteredTasks.length} 个任务\n`);
  }
}

// 使用示例
const taskManager = new TaskManager();

// 添加任务
taskManager.addTask('学习JavaScript数组', 'high');
taskManager.addTask('写项目文档', 'medium');
taskManager.addTask('买咖啡', 'low');

// 完成任务
taskManager.completeTask(taskManager.tasks[0].id);

// 设置筛选条件
taskManager.setFilter('status', 'active');
taskManager.setFilter('priority', 'high');

// 删除任务
taskManager.deleteTask(taskManager.tasks[1].id);

// 重置筛选
taskManager.setFilter('status', 'all');
taskManager.setFilter('priority', 'all');

案例3:数据分析仪表板

/**
 * 数据分析仪表板
 * 对数据集进行各种统计和分析
 */

class DataDashboard {
  constructor(data) {
    this.data = data;
  }

  // 基本统计信息
  getBasicStats() {
    const count = this.data.length;
    const sum = this.data.reduce((acc, val) => acc + val, 0);
    const mean = sum / count;
    
    // 计算标准差
    const squaredDiffs = this.data.map(val => Math.pow(val - mean, 2));
    const variance = squaredDiffs.reduce((acc, val) => acc + val, 0) / count;
    const stdDev = Math.sqrt(variance);
    
    // 排序找中位数
    const sortedData = [...this.data].sort((a, b) => a - b);
    const median = count % 2 === 0 
      ? (sortedData[count/2 - 1] + sortedData[count/2]) / 2
      : sortedData[Math.floor(count/2)];
    
    // 最小最大值
    const min = Math.min(...this.data);
    const max = Math.max(...this.data);
    
    return {
      count,
      sum,
      mean: parseFloat(mean.toFixed(2)),
      median: parseFloat(median.toFixed(2)),
      stdDev: parseFloat(stdDev.toFixed(2)),
      min,
      max,
      range: max - min
    };
  }

  // 数据分组统计
  groupData(binSize) {
    const min = Math.min(...this.data);
    const max = Math.max(...this.data);
    
    // 计算分组数量
    const binCount = Math.ceil((max - min) / binSize);
    
    // 初始化分组
    const bins = Array(binCount).fill().map((_, i) => ({
      min: min + i * binSize,
      max: min + (i + 1) * binSize,
      count: 0,
      values: []
    }));
    
    // 填充数据到分组
    this.data.forEach(value => {
      // 找到合适的分组
      let binIndex = Math.floor((value - min) / binSize);
      // 处理最大值的情况
      binIndex = binIndex >= binCount ? binCount - 1 : binIndex;
      
      bins[binIndex].count++;
      bins[binIndex].values.push(value);
    });
    
    return bins;
  }

  // 数据过滤
  filterData(minValue, maxValue) {
    return this.data.filter(value => value >= minValue && value <= maxValue);
  }

  // 数据标准化 (0-1)
  normalizeData() {
    const min = Math.min(...this.data);
    const max = Math.max(...this.data);
    const range = max - min;
    
    return this.data.map(value => (value - min) / range);
  }

  // 显示数据分布
  showDistribution(binSize = 10) {
    const stats = this.getBasicStats();
    const bins = this.groupData(binSize);
    
    console.log('\n=== 数据分布分析 ===');
    console.log(`数据集大小: ${stats.count}`);
    console.log(`最小值: ${stats.min}, 最大值: ${stats.max}, 平均值: ${stats.mean}`);
    console.log(`中位数: ${stats.median}, 标准差: ${stats.stdDev}`);
    
    console.log('\n分组统计:');
    bins.forEach((bin, i) => {
      const percentage = ((bin.count / stats.count) * 100).toFixed(1);
      const bar = '■'.repeat(Math.round(percentage / 5)); // 每5%一个方块
      
      console.log(
        `${i + 1}. [${bin.min.toFixed(1)}-${bin.max.toFixed(1)}): ` +
        `${bin.count} 项 (${percentage}%) ${bar}`
      );
    });
  }
}

// 使用示例
// 生成随机测试数据 (100个0-100之间的随机数)
const testData = Array.from({ length: 100 }, () => Math.floor(Math.random() * 100));

const dashboard = new DataDashboard(testData);

// 显示基本统计信息
console.log('基本统计信息:', dashboard.getBasicStats());

// 显示数据分布
dashboard.showDistribution(10);

// 数据过滤
const filteredData = dashboard.filterData(30, 70);
console.log('\n过滤后的数据(30-70):', filteredData.length, '项');

// 数据标准化
const normalizedData = dashboard.normalizeData();
console.log('\n标准化后的前10项数据:', normalizedData.slice(0, 10).map(v => v.toFixed(2)));

案例4:图片轮播组件

/**
 * 图片轮播组件
 * 实现自动轮播、手动切换、指示器导航等功能
 */

class ImageSlider {
  constructor(containerId, images, options = {}) {
    this.container = document.getElementById(containerId);
    this.images = images;
    this.currentIndex = 0;
    this.intervalId = null;
    
    // 默认配置
    this.options = {
      interval: 3000,
      transition: 500,
      showDots: true,
      showArrows: true,
      ...options
    };
    
    // 初始化
    this.init();
  }

  // 初始化轮播
  init() {
    // 创建HTML结构
    this.container.innerHTML = `
      <div class="slider-container">
        <div class="slider-track"></div>
        ${this.options.showArrows ? `
          <button class="slider-arrow prev">❮</button>
          <button class="slider-arrow next">❯</button>
        ` : ''}
        ${this.options.showDots ? '<div class="slider-dots"></div>' : ''}
      </div>
    `;
    
    this.track = this.container.querySelector('.slider-track');
    this.track.style.transition = `${this.options.transition}ms`;
    
    // 添加图片
    this.images.forEach((image, index) => {
      const slide = document.createElement('div');
      slide.className = 'slide';
      slide.innerHTML = `<img src="${image.url}" alt="${image.alt || ''}">`;
      this.track.appendChild(slide);
    });
    
    // 添加指示器
    if (this.options.showDots) {
      this.dotsContainer = this.container.querySelector('.slider-dots');
      this.images.forEach((_, index) => {
        const dot = document.createElement('button');
        dot.className = 'dot';
        if (index === 0) dot.classList.add('active');
        dot.addEventListener('click', () => this.goToSlide(index));
        this.dotsContainer.appendChild(dot);
      });
    }
    
    // 添加箭头事件
    if (this.options.showArrows) {
      this.prevBtn = this.container.querySelector('.prev');
      this.nextBtn = this.container.querySelector('.next');
      
      this.prevBtn.addEventListener('click', () => this.prevSlide());
      this.nextBtn.addEventListener('click', () => this.nextSlide());
    }
    
    // 设置初始位置
    this.updateSlider();
    
    // 开始自动轮播
    this.startAutoPlay();
    
    // 鼠标悬停暂停
    this.container.addEventListener('mouseenter', () => this.stopAutoPlay());
    this.container.addEventListener('mouseleave', () => this.startAutoPlay());
  }

  // 更新滑块位置
  updateSlider() {
    const slideWidth = this.container.offsetWidth;
    this.track.style.transform = `translateX(-${this.currentIndex * slideWidth}px)`;
    
    // 更新指示器状态
    if (this.options.showDots) {
      const dots = this.dotsContainer.querySelectorAll('.dot');
      dots.forEach((dot, index) => {
        dot.classList.toggle('active', index === this.currentIndex);
      });
    }
  }

  // 下一张
  nextSlide() {
    this.currentIndex = (this.currentIndex + 1) % this.images.length;
    this.updateSlider();
  }

  // 上一张
  prevSlide() {
    this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
    this.updateSlider();
  }

  // 跳转到指定幻灯片
  goToSlide(index) {
    this.currentIndex = index;
    this.updateSlider();
  }

  // 开始自动播放
  startAutoPlay() {
    if (this.intervalId) return;
    
    this.intervalId = setInterval(() => {
      this.nextSlide();
    }, this.options.interval);
  }

  // 停止自动播放
  stopAutoPlay() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
  }

  // 响应窗口大小变化
  handleResize() {
    this.updateSlider();
  }
}

// 使用示例
document.addEventListener('DOMContentLoaded', () => {
  const images = [
    { url: 'https://via.placeholder.com/800x400?text=Slide+1', alt: 'Slide 1' },
    { url: 'https://via.placeholder.com/800x400?text=Slide+2', alt: 'Slide 2' },
    { url: 'https://via.placeholder.com/800x400?text=Slide+3', alt: 'Slide 3' },
    { url: 'https://via.placeholder.com/800x400?text=Slide+4', alt: 'Slide 4' }
  ];
  
  const slider = new ImageSlider('slider-container', images, {
    interval: 2000,
    transition: 600,
    showDots: true,
    showArrows: true
  });
  
  // 响应窗口大小变化
  window.addEventListener('resize', () => slider.handleResize());
});

// 对应的HTML结构:
/*
<div id="slider-container" style="width: 800px; height: 400px; overflow: hidden; position: relative;"></div>

<style>
  .slider-container {
    position: relative;
    width: 100%;
    height: 100%;
  }
  
  .slider-track {
    display: flex;
    height: 100%;
  }
  
  .slide {
    min-width: 100%;
    height: 100%;
  }
  
  .slide img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  
  .slider-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background: rgba(0,0,0,0.5);
    color: white;
    border: none;
    padding: 10px 15px;
    cursor: pointer;
    font-size: 18px;
    z-index: 10;
  }
  
  .prev {
    left: 10px;
  }
  
  .next {
    right: 10px;
  }
  
  .slider-dots {
    position: absolute;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 10px;
  }
  
  .dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: rgba(255,255,255,0.5);
    border: none;
    cursor: pointer;
    padding: 0;
  }
  
  .dot.active {
    background: white;
  }
</style>
*/

案例5:表格排序与筛选

/**
 * 表格排序与筛选组件
 * 实现表格数据的多列排序、筛选和分页功能
 */

class TableManager {
  constructor(tableId, data, options = {}) {
    this.table = document.getElementById(tableId);
    this.originalData = data;
    this.currentData = [...data];
    this.sortState = {}; // 存储各列排序状态
    this.currentPage = 1;
    
    // 默认配置
    this.options = {
      pageSize: 10,
      sortable: true,
      filterable: true,
      pagination: true,
      ...options
    };
    
    // 初始化
    this.initTable();
    this.renderTable();
    
    if (this.options.pagination) {
      this.renderPagination();
    }
  }

  // 初始化表格结构
  initTable() {
    // 清空表格
    this.table.innerHTML = '';
    
    // 创建表头
    const thead = document.createElement('thead');
    const headerRow = document.createElement('tr');
    
    // 获取列配置或从数据第一项推断
    const columns = this.options.columns || 
      Object.keys(this.originalData[0] || {}).map(key => ({
        key,
        title: key.charAt(0).toUpperCase() + key.slice(1),
        sortable: true,
        filterable: true
      }));
    
    this.columns = columns;
    
    // 添加表头单元格
    columns.forEach(column => {
      const th = document.createElement('th');
      th.textContent = column.title;
      
      if (this.options.sortable && column.sortable !== false) {
        th.style.cursor = 'pointer';
        th.addEventListener('click', () => this.sortData(column.key));
        
        // 初始化排序状态
        this.sortState[column.key] = 0; // 0: 未排序, 1: 升序, -1: 降序
      }
      
      headerRow.appendChild(th);
    });
    
    thead.appendChild(headerRow);
    this.table.appendChild(thead);
    
    // 创建表体
    this.tbody = document.createElement('tbody');
    this.table.appendChild(this.tbody);
    
    // 添加筛选行
    if (this.options.filterable) {
      const filterRow = document.createElement('tr');
      filterRow.className = 'filter-row';
      
      columns.forEach(column => {
        const td = document.createElement('td');
        
        if (column.filterable !== false) {
          const input = document.createElement('input');
          input.type = 'text';
          input.placeholder = `筛选 ${column.title}`;
          input.addEventListener('input', (e) => {
            this.filterData(column.key, e.target.value);
          });
          td.appendChild(input);
        }
        
        filterRow.appendChild(td);
      });
      
      thead.appendChild(filterRow);
    }
  }

  // 渲染表格数据
  renderTable() {
    // 清空表体
    this.tbody.innerHTML = '';
    
    // 计算分页数据
    const start = (this.currentPage - 1) * this.options.pageSize;
    const end = start + this.options.pageSize;
    const pageData = this.currentData.slice(start, end);
    
    // 添加数据行
    pageData.forEach(row => {
      const tr = document.createElement('tr');
      
      this.columns.forEach(column => {
        const td = document.createElement('td');
        td.textContent = row[column.key];
        tr.appendChild(td);
      });
      
      this.tbody.appendChild(tr);
    });
    
    // 如果没有数据,显示空行
    if (pageData.length === 0) {
      const tr = document.createElement('tr');
      const td = document.createElement('td');
      td.colSpan = this.columns.length;
      td.textContent = '没有数据';
      tr.appendChild(td);
      this.tbody.appendChild(tr);
    }
  }

  // 渲染分页控件
  renderPagination() {
    // 移除旧的分页控件
    const oldPagination = this.table.nextElementSibling;
    if (oldPagination && oldPagination.classList.contains('pagination')) {
      oldPagination.remove();
    }
    
    const totalPages = Math.ceil(this.currentData.length / this.options.pageSize);
    
    // 如果不需要分页或只有一页,则不显示分页控件
    if (totalPages <= 1) return;
    
    const pagination = document.createElement('div');
    pagination.className = 'pagination';
    
    // 上一页按钮
    const prevBtn = document.createElement('button');
    prevBtn.textContent = '上一页';
    prevBtn.disabled = this.currentPage === 1;
    prevBtn.addEventListener('click', () => {
      this.currentPage--;
      this.renderTable();
      this.renderPagination();
    });
    pagination.appendChild(prevBtn);
    
    // 页码按钮
    const maxVisiblePages = 5;
    let startPage = Math.max(1, this.currentPage - Math.floor(maxVisiblePages / 2));
    let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
    
    // 调整起始页码,确保显示maxVisiblePages个页码
    if (endPage - startPage + 1 < maxVisiblePages) {
      startPage = Math.max(1, endPage - maxVisiblePages + 1);
    }
    
    // 第一页
    if (startPage > 1) {
      const firstPageBtn = document.createElement('button');
      firstPageBtn.textContent = '1';
      firstPageBtn.addEventListener('click', () => {
        this.currentPage = 1;
        this.renderTable();
        this.renderPagination();
      });
      pagination.appendChild(firstPageBtn);
      
      if (startPage > 2) {
        const ellipsis = document.createElement('span');
        ellipsis.textContent = '...';
        pagination.appendChild(ellipsis);
      }
    }
    
    // 中间页码
    for (let i = startPage; i <= endPage; i++) {
      const pageBtn = document.createElement('button');
      pageBtn.textContent = i;
      pageBtn.className = i === this.currentPage ? 'active' : '';
      pageBtn.addEventListener('click', () => {
        this.currentPage = i;
        this.renderTable();
        this.renderPagination();
      });
      pagination.appendChild(pageBtn);
    }
    
    // 最后一页
    if (endPage < totalPages) {
      if (endPage < totalPages - 1) {
        const ellipsis = document.createElement('span');
        ellipsis.textContent = '...';
        pagination.appendChild(ellipsis);
      }
      
      const lastPageBtn = document.createElement('button');
      lastPageBtn.textContent = totalPages;
      lastPageBtn.addEventListener('click', () => {
        this.currentPage = totalPages;
        this.renderTable();
        this.renderPagination();
      });
      pagination.appendChild(lastPageBtn);
    }
    
    // 下一页按钮
    const nextBtn = document.createElement('button');
    nextBtn.textContent = '下一页';
    nextBtn.disabled = this.currentPage === totalPages;
    nextBtn.addEventListener('click', () => {
      this.currentPage++;
      this.renderTable();
      this.renderPagination();
    });
    pagination.appendChild(nextBtn);
    
    // 添加到表格后面
    this.table.insertAdjacentElement('afterend', pagination);
  }

  // 排序数据
  sortData(columnKey) {
    // 切换排序状态: 无排序 -> 升序 -> 降序 -> 无排序
    this.sortState[columnKey] = ((this.sortState[columnKey] || 0) + 2) % 3 - 1;
    
    // 重置其他列的排序状态
    Object.keys(this.sortState).forEach(key => {
      if (key !== columnKey) {
        this.sortState[key] = 0;
      }
    });
    
    if (this.sortState[columnKey] === 0) {
      // 无排序,恢复原始顺序
      this.currentData = [...this.originalData];
    } else {
      // 排序数据
      const sortOrder = this.sortState[columnKey];
      this.currentData.sort((a, b) => {
        // 处理可能的undefined或null值
        const valA = a[columnKey] !== undefined ? a[columnKey] : '';
        const valB = b[columnKey] !== undefined ? b[columnKey] : '';
        
        // 数字和字符串比较
        if (typeof valA === 'number' && typeof valB === 'number') {
          return (valA - valB) * sortOrder;
        }
        
        return valA.toString().localeCompare(valB.toString()) * sortOrder;
      });
    }
    
    // 重置到第一页
    this.currentPage = 1;
    
    // 重新渲染
    this.renderTable();
    this.renderPagination();
    
    // 更新表头排序指示器
    this.updateSortIndicators();
  }

  // 更新表头排序指示器
  updateSortIndicators() {
    const headers = this.table.querySelectorAll('th');
    
    headers.forEach((header, index) => {
      const columnKey = this.columns[index].key;
      header.textContent = this.columns[index].title;
      
      if (this.sortState[columnKey] === 1) {
        header.textContent += ' ↑';
      } else if (this.sortState[columnKey] === -1) {
        header.textContent += ' ↓';
      }
    });
  }

  // 筛选数据
  filterData(columnKey, searchText) {
    // 重置排序状态
    this.sortState = {};
    this.updateSortIndicators();
    
    // 重置到第一页
    this.currentPage = 1;
    
    if (!searchText) {
      // 没有筛选文本,恢复原始数据
      this.currentData = [...this.originalData];
    } else {
      // 应用筛选
      const searchLower = searchText.toLowerCase();
      this.currentData = this.originalData.filter(item => {
        const value = item[columnKey] !== undefined ? item[columnKey] : '';
        return value.toString().toLowerCase().includes(searchLower);
      });
    }
    
    // 重新渲染
    this.renderTable();
    this.renderPagination();
  }
}

// 使用示例
document.addEventListener('DOMContentLoaded', () => {
  // 示例数据
  const employeeData = [
    { id: 1, name: '张三', department: '研发部', salary: 12000, joinDate: '2020-05-15' },
    { id: 2, name: '李四', department: '市场部', salary: 8500, joinDate: '2019-11-03' },
    { id: 3, name: '王五', department: '研发部', salary: 15000, joinDate: '2018-07-22' },
    { id: 4, name: '赵六', department: '人事部', salary: 7500, joinDate: '2021-02-18' },
    { id: 5, name: '钱七', department: '市场部', salary: 9200, joinDate: '2020-09-30' },
    { id: 6, name: '孙八', department: '研发部', salary: 11000, joinDate: '2019-04-12' },
    { id: 7, name: '周九', department: '财务部', salary: 13000, joinDate: '2017-12-05' },
    { id: 8, name: '吴十', department: '人事部', salary: 6800, joinDate: '2022-01-20' },
    { id: 9, name: '郑十一', department: '研发部', salary: 14000, joinDate: '2018-10-15' },
    { id: 10, name: '王十二', department: '市场部', salary: 8800, joinDate: '2020-07-08' },
    { id: 11, name: '李十三', department: '财务部', salary: 12500, joinDate: '2019-08-25' },
    { id: 12, name: '张十四', department: '研发部', salary: 11500, joinDate: '2021-05-14' }
  ];
  
  // 初始化表格
  const tableManager = new TableManager('employee-table', employeeData, {
    pageSize: 5,
    columns: [
      { key: 'id', title: 'ID', sortable: true, filterable: false },
      { key: 'name', title: '姓名', sortable: true, filterable: true },
      { key: 'department', title: '部门', sortable: true, filterable: true },
      { key: 'salary', title: '薪资', sortable: true, filterable: false },
      { key: 'joinDate', title: '入职日期', sortable: true, filterable: false }
    ]
  });
});

// 对应的HTML结构:
/*
<table id="employee-table"></table>

<style>
  #employee-table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 20px;
  }
  
  #employee-table th, #employee-table td {
    border: 1px solid #ddd;
    padding: 8px 12px;
    text-align: left;
  }
  
  #employee-table th {
    background-color: #f2f2f2;
    cursor: pointer;
  }
  
  #employee-table th:hover {
    background-color: #e6e6e6;
  }
  
  #employee-table tr:nth-child(even) {
    background-color: #f9f9f9;
  }
  
  #employee-table tr:hover {
    background-color: #f1f1f1;
  }
  
  .filter-row input {
    width: 100%;
    padding: 5px;
    box-sizing: border-box;
  }
  
  .pagination {
    display: flex;
    gap: 5px;
    margin-top: 10px;
  }
  
  .pagination button {
    padding: 5px 10px;
    cursor: pointer;
  }
  
  .pagination button.active {
    background-color: #4CAF50;
    color: white;
    border: none;
  }
  
  .pagination button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
  
  .pagination span {
    padding: 5px 10px;
  }
</style>
*/

这些案例涵盖了JavaScript数组在实际开发中的多种应用场景,包括:

  1. 购物车功能 - 展示数组的增删改查和计算操作
  2. 任务管理系统 - 展示数组的筛选、排序和状态管理
  3. 数据分析仪表板 - 展示数组的统计和分组操作
  4. 图片轮播组件 - 展示数组在UI组件中的应用
  5. 表格排序与筛选 - 展示复杂的数据处理和交互

相关文章:

  • 三大开源大模型应用框架深度对比:AnythingLLM、Dify 与 Open-WebUI
  • XML、JSON 和 Protocol Buffers (protobuf) 对比
  • ESP32开发入门:基于VSCode+PlatformIO环境搭建指南
  • 在Oracle数据库中,一条SQL查询请求的执行过程是一个复杂且精细的流水线
  • CSS学习02 动态列数表格开发,解决多组数据布局与边框重合问题
  • WEB前端开发中常用的算法
  • 布局决定终局:基于开源AI大模型、AI智能名片与S2B2C商城小程序的战略反推思维
  • 从Function Calling到Tool Calling:Spring AI架构升级背后的技术考量
  • Git 高级操作
  • VMWare Workstation 17 安装 Ubuntu24.02
  • pycharm如何通过跳板机连接服务器在本地debug
  • Pytorch torch.utils.data.dataloader.default_collate 介绍
  • CTF web入门之命令执行 完整版
  • Git代码管理
  • 代码随想录-06-二叉树-05.01 二叉树的层序遍历
  • 【股票数据API接口19】如何获取股票主力资金走势数据之Python、Java等多种主流语言实例代码演示通过股票数据接口获取数据
  • 【力扣hot100题】(088)分割等和子集
  • MySQL-多表查询-自连接联合查询子查询
  • Leetcode131:分割回文串——回溯算法
  • 【Linux】线程池与封装线程
  • asp.net 动态网站开发教程/合肥网站建设优化
  • 众创空间网站建设方案/站长之家音效
  • 百度云分享tp响应式网站开发/微信营销工具
  • 网站下拉框怎么做/emlog友情链接代码
  • 网站建设流程时间表/正规网站建设服务
  • 黄石网站建设/软文什么意思