JS 字符串与数组操作
2025/9/17大约 12 分钟
JavaScript字符串与数组操作
学习目标
- 掌握字符串的各种处理方法
- 熟练使用数组的增删改查操作
- 理解数组遍历的不同方式
- 学会选择合适的方法处理数据
字符串方法详解
字符串截取方法
let text = 'Hello JavaScript World';
// charAt() - 获取指定位置的字符
console.log(text.charAt(0)); // 'H'
console.log(text.charAt(6)); // 'J'
console.log(text.charAt(100)); // '' (超出范围返回空字符串)
// substring() - 截取字符串片段
console.log(text.substring(0, 5)); // 'Hello'
console.log(text.substring(6, 16)); // 'JavaScript'
console.log(text.substring(6)); // 'JavaScript World' (从位置6到末尾)
// substr() - 从指定位置开始截取指定长度
console.log(text.substr(0, 5)); // 'Hello'
console.log(text.substr(6, 10)); // 'JavaScript'
console.log(text.substr(-5)); // 'World' (负数从末尾开始)
// slice() - 类似substring,但支持负数索引
console.log(text.slice(0, 5)); // 'Hello'
console.log(text.slice(-5)); // 'World'
console.log(text.slice(-11, -6)); // 'Script'
方法对比
substring()
: 不支持负数,负数会被当作0处理substr()
: 第二个参数是长度,支持负数起始位置slice()
: 支持负数索引,功能最全面(推荐使用)
字符串查找方法
let message = 'JavaScript is awesome, JavaScript is powerful';
// indexOf() - 查找子字符串首次出现的位置
console.log(message.indexOf('JavaScript')); // 0
console.log(message.indexOf('is')); // 11
console.log(message.indexOf('Python')); // -1 (未找到)
console.log(message.indexOf('JavaScript', 1)); // 23 (从位置1开始查找)
// lastIndexOf() - 查找子字符串最后出现的位置
console.log(message.lastIndexOf('JavaScript')); // 23
console.log(message.lastIndexOf('is')); // 34
// includes() - 检查是否包含子字符串 (ES6)
console.log(message.includes('awesome')); // true
console.log(message.includes('Python')); // false
console.log(message.includes('JAVASCRIPT')); // false (区分大小写)
// startsWith() - 检查是否以指定字符串开头 (ES6)
console.log(message.startsWith('JavaScript')); // true
console.log(message.startsWith('Java')); // true
console.log(message.startsWith('Script')); // false
// endsWith() - 检查是否以指定字符串结尾 (ES6)
console.log(message.endsWith('powerful')); // true
console.log(message.endsWith('ful')); // true
console.log(message.endsWith('awesome')); // false
字符串转换方法
let original = ' Hello World ';
// trim() - 去除首尾空白字符
console.log(original.trim()); // 'Hello World'
console.log(original.trimStart()); // 'Hello World ' (ES2019)
console.log(original.trimEnd()); // ' Hello World' (ES2019)
// 大小写转换
let text = 'JavaScript Programming';
console.log(text.toUpperCase()); // 'JAVASCRIPT PROGRAMMING'
console.log(text.toLowerCase()); // 'javascript programming'
// replace() - 替换字符串
let sentence = 'I love Python. Python is great!';
console.log(sentence.replace('Python', 'JavaScript'));
// 'I love JavaScript. Python is great!' (只替换第一个)
console.log(sentence.replace(/Python/g, 'JavaScript'));
// 'I love JavaScript. JavaScript is great!' (全局替换)
// split() - 分割字符串为数组
let csv = 'apple,banana,orange,grape';
console.log(csv.split(',')); // ['apple', 'banana', 'orange', 'grape']
console.log(csv.split(',', 2)); // ['apple', 'banana'] (限制数量)
let words = 'Hello World JavaScript';
console.log(words.split(' ')); // ['Hello', 'World', 'JavaScript']
console.log(words.split('')); // ['H','e','l','l','o',' ','W',...] (每个字符)
字符串拼接方法
// concat() - 连接字符串
let str1 = 'Hello';
let str2 = 'World';
console.log(str1.concat(' ', str2)); // 'Hello World'
console.log(str1.concat(' ', str2, '!')); // 'Hello World!'
// 模板字符串 (推荐)
let name = '张三';
let age = 25;
let intro = `我叫${name},今年${age}岁,${age >= 18 ? '已成年' : '未成年'}`;
console.log(intro); // '我叫张三,今年25岁,已成年'
// 字符串重复 (ES6)
console.log('Ha'.repeat(3)); // 'HaHaHa'
console.log('-'.repeat(10)); // '----------'
数组基础操作
数组创建和基本属性
// 数组创建方式
let arr1 = []; // 空数组
let arr2 = [1, 2, 3, 4, 5]; // 字面量方式
let arr3 = new Array(5); // 创建长度为5的空数组
let arr4 = new Array(1, 2, 3); // 创建包含元素的数组
// 数组长度
console.log(arr2.length); // 5
arr2.length = 3; // 截断数组
console.log(arr2); // [1, 2, 3]
// 检查是否为数组
console.log(Array.isArray(arr2)); // true
console.log(Array.isArray('hello')); // false
console.log(typeof arr2); // 'object' (不够准确)
数组遍历方法
let fruits = ['苹果', '香蕉', '橙子', '葡萄'];
// 传统for循环
for (let i = 0; i < fruits.length; i++) {
console.log(`索引${i}: ${fruits[i]}`);
}
// for...of循环 (ES6,推荐)
for (let fruit of fruits) {
console.log(fruit);
}
// for...in循环 (遍历索引)
for (let index in fruits) {
console.log(`${index}: ${fruits[index]}`);
}
// forEach方法 (函数式编程)
fruits.forEach(function(fruit, index) {
console.log(`${index}: ${fruit}`);
});
// 箭头函数简化
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});
数组增删操作
let numbers = [1, 2, 3];
// push() - 在末尾添加元素
numbers.push(4); // [1, 2, 3, 4]
numbers.push(5, 6); // [1, 2, 3, 4, 5, 6]
console.log(numbers.push(7)); // 返回新长度: 7
// pop() - 删除末尾元素
let lastElement = numbers.pop(); // 返回被删除的元素: 7
console.log(numbers); // [1, 2, 3, 4, 5, 6]
// unshift() - 在开头添加元素
numbers.unshift(0); // [0, 1, 2, 3, 4, 5, 6]
numbers.unshift(-2, -1); // [-2, -1, 0, 1, 2, 3, 4, 5, 6]
// shift() - 删除开头元素
let firstElement = numbers.shift(); // 返回被删除的元素: -2
console.log(numbers); // [-1, 0, 1, 2, 3, 4, 5, 6]
splice() 方法详解
splice()
是最强大的数组方法,可以实现增删改操作:
let animals = ['猫', '狗', '鸟', '鱼', '兔子'];
// 删除元素: splice(起始位置, 删除数量)
let removed = animals.splice(1, 2); // 从索引1开始删除2个元素
console.log(removed); // ['狗', '鸟'] (被删除的元素)
console.log(animals); // ['猫', '鱼', '兔子']
// 插入元素: splice(位置, 0, 新元素...)
animals.splice(1, 0, '马', '牛'); // 在索引1处插入元素
console.log(animals); // ['猫', '马', '牛', '鱼', '兔子']
// 替换元素: splice(位置, 删除数量, 新元素...)
animals.splice(2, 1, '羊'); // 替换索引2的元素
console.log(animals); // ['猫', '马', '羊', '鱼', '兔子']
// 从末尾操作 (负数索引)
animals.splice(-1, 1, '龙'); // 替换最后一个元素
console.log(animals); // ['猫', '马', '羊', '鱼', '龙']
注意
splice()
方法会修改原数组,如果需要保持原数组不变,请使用其他方法或先复制数组。
数组查找方法
let numbers = [1, 3, 5, 7, 9, 3, 11];
// indexOf() - 查找元素首次出现的索引
console.log(numbers.indexOf(5)); // 2
console.log(numbers.indexOf(3)); // 1 (第一次出现)
console.log(numbers.indexOf(10)); // -1 (未找到)
// lastIndexOf() - 查找元素最后出现的索引
console.log(numbers.lastIndexOf(3)); // 5
// includes() - 检查是否包含某元素 (ES2016)
console.log(numbers.includes(7)); // true
console.log(numbers.includes(10)); // false
// find() - 查找满足条件的第一个元素 (ES6)
let found = numbers.find(num => num > 5);
console.log(found); // 7
// findIndex() - 查找满足条件的第一个元素的索引 (ES6)
let foundIndex = numbers.findIndex(num => num > 5);
console.log(foundIndex); // 3
数组转换方法
let fruits = ['苹果', '香蕉', '橙子'];
let numbers = [1, 2, 3, 4, 5];
// join() - 将数组元素连接成字符串
console.log(fruits.join()); // '苹果,香蕉,橙子' (默认逗号分隔)
console.log(fruits.join(' | ')); // '苹果 | 香蕉 | 橙子'
console.log(fruits.join('')); // '苹果香蕉橙子'
// concat() - 连接数组
let moreFruits = ['葡萄', '西瓜'];
let allFruits = fruits.concat(moreFruits);
console.log(allFruits); // ['苹果', '香蕉', '橙子', '葡萄', '西瓜']
console.log(fruits); // 原数组不变
// 使用展开运算符连接 (ES6)
let combined = [...fruits, ...moreFruits];
console.log(combined); // ['苹果', '香蕉', '橙子', '葡萄', '西瓜']
// reverse() - 反转数组 (修改原数组)
let reversed = numbers.reverse();
console.log(reversed); // [5, 4, 3, 2, 1]
console.log(numbers); // 原数组也被修改
// 不修改原数组的反转
let originalNumbers = [1, 2, 3, 4, 5];
let reversedCopy = [...originalNumbers].reverse();
console.log(originalNumbers); // [1, 2, 3, 4, 5] (不变)
console.log(reversedCopy); // [5, 4, 3, 2, 1]
数组高级方法
数组过滤和映射
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// filter() - 过滤数组元素
let evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4, 6, 8, 10]
let bigNumbers = numbers.filter(num => num > 5);
console.log(bigNumbers); // [6, 7, 8, 9, 10]
// map() - 映射转换数组元素
let doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
let squared = numbers.map(num => num ** 2);
console.log(squared); // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
// 链式调用
let result = numbers
.filter(num => num % 2 === 0) // 过滤偶数
.map(num => num * 3) // 乘以3
.filter(num => num > 10); // 过滤大于10的数
console.log(result); // [12, 18, 24, 30]
数组检测方法
let scores = [85, 92, 78, 96, 88];
let mixedArray = [1, '2', 3, '4', 5];
// every() - 检查是否所有元素都满足条件
let allPassed = scores.every(score => score >= 60);
console.log(allPassed); // true
let allNumbers = mixedArray.every(item => typeof item === 'number');
console.log(allNumbers); // false
// some() - 检查是否至少有一个元素满足条件
let hasExcellent = scores.some(score => score >= 90);
console.log(hasExcellent); // true
let hasString = mixedArray.some(item => typeof item === 'string');
console.log(hasString); // true
实际应用示例
字符串处理实例
用户输入验证和格式化
// 用户输入处理函数
function processUserInput(input) {
// 去除首尾空格
let cleaned = input.trim();
// 检查是否为空
if (!cleaned) {
return { valid: false, message: '输入不能为空' };
}
// 检查长度
if (cleaned.length < 2) {
return { valid: false, message: '输入长度至少2个字符' };
}
// 首字母大写
let formatted = cleaned.charAt(0).toUpperCase() + cleaned.slice(1).toLowerCase();
return { valid: true, value: formatted };
}
// 测试
console.log(processUserInput(' hello world ')); // {valid: true, value: 'Hello world'}
console.log(processUserInput('')); // {valid: false, message: '输入不能为空'}
console.log(processUserInput('a')); // {valid: false, message: '输入长度至少2个字符'}
// 邮箱验证
function validateEmail(email) {
let trimmed = email.trim().toLowerCase();
// 简单的邮箱格式检查
if (!trimmed.includes('@')) {
return false;
}
let parts = trimmed.split('@');
if (parts.length !== 2) {
return false;
}
let [username, domain] = parts;
return username.length > 0 && domain.includes('.');
}
// 测试
console.log(validateEmail('user@example.com')); // true
console.log(validateEmail('invalid-email')); // false
console.log(validateEmail('user@domain')); // false
数组数据处理实例
学生成绩管理系统
// 学生数据
let students = [
{ name: '张三', scores: [85, 92, 78, 96] },
{ name: '李四', scores: [76, 88, 82, 90] },
{ name: '王五', scores: [95, 87, 91, 89] },
{ name: '赵六', scores: [68, 75, 72, 80] }
];
// 计算每个学生的平均分
function calculateAverages(students) {
return students.map(student => {
let average = student.scores.reduce((sum, score) => sum + score, 0) / student.scores.length;
return {
...student,
average: Math.round(average * 100) / 100 // 保留两位小数
};
});
}
// 按平均分排序
function sortByAverage(students) {
return students.sort((a, b) => b.average - a.average);
}
// 筛选优秀学生 (平均分>=85)
function getExcellentStudents(students) {
return students.filter(student => student.average >= 85);
}
// 获取班级统计信息
function getClassStats(students) {
let averages = students.map(s => s.average);
let classAverage = averages.reduce((sum, avg) => sum + avg, 0) / averages.length;
let highest = Math.max(...averages);
let lowest = Math.min(...averages);
return {
classAverage: Math.round(classAverage * 100) / 100,
highest,
lowest,
totalStudents: students.length
};
}
// 执行处理
let studentsWithAvg = calculateAverages(students);
let sortedStudents = sortByAverage(studentsWithAvg);
let excellentStudents = getExcellentStudents(studentsWithAvg);
let classStats = getClassStats(studentsWithAvg);
console.log('学生平均分:', studentsWithAvg);
console.log('按成绩排序:', sortedStudents);
console.log('优秀学生:', excellentStudents);
console.log('班级统计:', classStats);
购物车功能实现
购物车管理
// 购物车类
class ShoppingCart {
constructor() {
this.items = [];
}
// 添加商品
addItem(product, quantity = 1) {
let existingItem = this.items.find(item => item.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({
...product,
quantity
});
}
}
// 删除商品
removeItem(productId) {
this.items = this.items.filter(item => item.id !== productId);
}
// 更新数量
updateQuantity(productId, quantity) {
let item = this.items.find(item => item.id === productId);
if (item) {
if (quantity <= 0) {
this.removeItem(productId);
} else {
item.quantity = quantity;
}
}
}
// 计算总价
getTotal() {
return this.items.reduce((total, item) => {
return total + (item.price * item.quantity);
}, 0);
}
// 获取商品总数
getTotalItems() {
return this.items.reduce((total, item) => total + item.quantity, 0);
}
// 清空购物车
clear() {
this.items = [];
}
// 获取购物车摘要
getSummary() {
return {
items: this.items,
totalItems: this.getTotalItems(),
totalPrice: this.getTotal()
};
}
}
// 使用示例
let cart = new ShoppingCart();
// 商品数据
let products = [
{ id: 1, name: 'iPhone 14', price: 5999 },
{ id: 2, name: 'MacBook Pro', price: 12999 },
{ id: 3, name: 'AirPods', price: 1299 }
];
// 添加商品到购物车
cart.addItem(products[0], 1); // iPhone 14 x1
cart.addItem(products[1], 1); // MacBook Pro x1
cart.addItem(products[2], 2); // AirPods x2
console.log('购物车摘要:', cart.getSummary());
// 输出: { items: [...], totalItems: 4, totalPrice: 21596 }
// 更新数量
cart.updateQuantity(3, 1); // AirPods改为1个
// 删除商品
cart.removeItem(2); // 删除MacBook Pro
console.log('更新后的购物车:', cart.getSummary());
最佳实践
字符串处理建议
// 1. 使用模板字符串而不是字符串拼接
// ❌ 不推荐
let message = 'Hello ' + name + ', you have ' + count + ' messages.';
// ✅ 推荐
let message = `Hello ${name}, you have ${count} messages.`;
// 2. 链式调用处理字符串
let processedText = userInput
.trim()
.toLowerCase()
.replace(/\s+/g, ' ') // 替换多个空格为单个空格
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
// 3. 使用正则表达式进行复杂匹配
function isValidPhone(phone) {
return /^1[3-9]\d{9}$/.test(phone);
}
数组操作建议
// 1. 优先使用函数式方法
// ❌ 传统循环
let doubled = [];
for (let i = 0; i < numbers.length; i++) {
doubled.push(numbers[i] * 2);
}
// ✅ 函数式方法
let doubled = numbers.map(num => num * 2);
// 2. 避免修改原数组
// ❌ 修改原数组
originalArray.sort();
// ✅ 创建副本
let sortedArray = [...originalArray].sort();
// 3. 使用合适的方法
// 查找单个元素用 find()
// 查找多个元素用 filter()
// 转换数组用 map()
// 检查条件用 some()/every()
练习题
练习1:文本分析器
编写一个文本分析器,统计文本的各种信息:
function analyzeText(text) {
// 清理文本
let cleaned = text.trim();
// 基本统计
let charCount = cleaned.length;
let charCountNoSpaces = cleaned.replace(/\s/g, '').length;
// 单词统计
let words = cleaned.split(/\s+/).filter(word => word.length > 0);
let wordCount = words.length;
// 句子统计
let sentences = cleaned.split(/[.!?]+/).filter(s => s.trim().length > 0);
let sentenceCount = sentences.length;
// 段落统计
let paragraphs = cleaned.split(/\n\s*\n/).filter(p => p.trim().length > 0);
let paragraphCount = paragraphs.length;
// 最常用的单词
let wordFreq = {};
words.forEach(word => {
let lowerWord = word.toLowerCase().replace(/[^a-zA-Z\u4e00-\u9fa5]/g, '');
if (lowerWord) {
wordFreq[lowerWord] = (wordFreq[lowerWord] || 0) + 1;
}
});
let mostFrequentWord = Object.keys(wordFreq).reduce((a, b) =>
wordFreq[a] > wordFreq[b] ? a : b, '');
return {
charCount,
charCountNoSpaces,
wordCount,
sentenceCount,
paragraphCount,
averageWordsPerSentence: Math.round(wordCount / sentenceCount * 100) / 100,
mostFrequentWord,
wordFrequency: wordFreq
};
}
// 测试
let sampleText = `
JavaScript是一种高级编程语言。它被广泛用于Web开发。
JavaScript具有动态类型和函数式编程特性。
学习JavaScript需要掌握基础语法。同时也要了解DOM操作。
JavaScript的应用场景非常广泛。
`;
console.log(analyzeText(sampleText));
练习2:数据排序和分组
实现一个通用的数据处理工具:
// 员工数据
let employees = [
{ name: '张三', department: '技术部', salary: 8000, age: 28 },
{ name: '李四', department: '销售部', salary: 6000, age: 25 },
{ name: '王五', department: '技术部', salary: 12000, age: 32 },
{ name: '赵六', department: '人事部', salary: 7000, age: 29 },
{ name: '钱七', department: '销售部', salary: 9000, age: 31 },
{ name: '孙八', department: '技术部', salary: 10000, age: 27 }
];
// 按部门分组
function groupBy(array, key) {
return array.reduce((groups, item) => {
let group = item[key];
groups[group] = groups[group] || [];
groups[group].push(item);
return groups;
}, {});
}
// 多字段排序
function sortBy(array, ...keys) {
return array.sort((a, b) => {
for (let key of keys) {
let direction = 1;
if (key.startsWith('-')) {
direction = -1;
key = key.slice(1);
}
if (a[key] < b[key]) return -1 * direction;
if (a[key] > b[key]) return 1 * direction;
}
return 0;
});
}
// 统计函数
function getStats(array, field) {
let values = array.map(item => item[field]);
return {
min: Math.min(...values),
max: Math.max(...values),
average: values.reduce((sum, val) => sum + val, 0) / values.length,
total: values.reduce((sum, val) => sum + val, 0)
};
}
// 使用示例
console.log('按部门分组:', groupBy(employees, 'department'));
console.log('按薪资降序排列:', sortBy([...employees], '-salary'));
console.log('薪资统计:', getStats(employees, 'salary'));
// 复合查询:技术部员工按年龄排序
let techEmployees = employees
.filter(emp => emp.department === '技术部')
.sort((a, b) => a.age - b.age);
console.log('技术部员工(按年龄):', techEmployees);
本章小结
通过本章学习,你应该掌握了:
- ✅ 字符串的各种处理方法和应用场景
- ✅ 数组的基本操作和高级方法
- ✅ 数组遍历的不同方式和选择原则
- ✅ 函数式编程在数组处理中的应用
- ✅ 实际项目中的字符串和数组处理技巧
字符串和数组是JavaScript中最常用的数据类型,熟练掌握它们的操作方法对于日常开发至关重要。
下一步
在下一章中,我们将学习JavaScript函数的高级特性和对象操作,包括函数参数、作用域、内置对象等重要概念。