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数组在实际开发中的多种应用场景,包括:
- 购物车功能 - 展示数组的增删改查和计算操作
- 任务管理系统 - 展示数组的筛选、排序和状态管理
- 数据分析仪表板 - 展示数组的统计和分组操作
- 图片轮播组件 - 展示数组在UI组件中的应用
- 表格排序与筛选 - 展示复杂的数据处理和交互