JavaScript数组与字符串方法深度解析
前言
在前端面试中,数组和字符串方法的考察频率极高,面试官不仅会考察你是否知道这些方法,更会关注你对方法细节的理解、应用场景的选择以及性能差异的认知。本文将深入剖析JavaScript中数组和字符串的各种方法,,详细讲解每个方法的参数、返回值、使用场景以及相似方法之间的区别,帮助你全面提升面试表现。
正文
1. JavaScript数组方法深度解析
1.1 增加元素的方法
push()
- 参数:一个或多个要添加的元素
- 返回值:数组的新长度
- 特点:直接修改原数组
- 面试题:
push和concat的区别是什么?(push修改原数组,concat返回新数组)push可以一次添加多个元素吗?(可以)
let arr = [1, 2];
let length = arr.push(3, 4); // arr变为[1,2,3,4],length为4
unshift()
- 参数:一个或多个要添加的元素
- 返回值:数组的新长度
- 特点:在数组开头添加,性能比
push差(需要移动所有元素) - 面试题:
- 大量数据插入时,
unshift和push哪个性能更好?(push更好) unshift能添加多个元素吗?(可以)
- 大量数据插入时,
let arr = [3, 4];
let length = arr.unshift(1, 2); // arr变为[1,2,3,4],length为4
splice()
- 参数:
- 第一个参数:开始修改的位置(负数表示倒数)
- 第二个参数:要删除的元素个数(0表示不删除)
- 后续参数:要添加的元素
- 返回值:由被删除元素组成的数组
- 特点:功能强大,可实现增删改
- 面试题:
- 如何用
splice实现数组去重? splice和slice的区别是什么?(splice修改原数组,slice不修改)
- 如何用
let arr = [1, 2, 5, 6];
let deleted = arr.splice(2, 0, 3, 4); // arr变为[1,2,3,4,5,6],deleted为[]
concat()
- 参数:数组或值
- 返回值:新数组
- 特点:不修改原数组,浅拷贝
- 面试题:
concat能合并多个数组吗?(可以)concat会深拷贝对象吗?(不会,是浅拷贝)
let arr1 = [1], arr2 = [2];
let newArr = arr1.concat(arr2, 3, [4,5]); // [1,2,3,4,5]
1.2 删除元素的方法
pop()
- 参数:无
- 返回值:被删除的元素
- 特点:修改原数组,空数组返回undefined
- 面试题:
- 如何用
pop实现栈结构? pop和shift的性能差异?(pop更好)
- 如何用
let arr = [1, 2, 3];
let last = arr.pop(); // arr变为[1,2],last为3
shift()
- 参数:无
- 返回值:被删除的元素
- 特点:修改原数组,性能较差(需要移动所有元素)
- 面试题:
- 如何用
shift实现队列结构? - 为什么
shift比pop慢?(需要移动所有元素)
- 如何用
let arr = [1, 2, 3];
let first = arr.shift(); // arr变为[2,3],first为1
splice()删除用法
- 面试题:
- 如何删除数组最后一个元素?(
arr.splice(-1,1)) - 如何删除数组中间元素?
- 如何删除数组最后一个元素?(
let arr = [1, 2, 3, 4];
let deleted = arr.splice(1, 2); // arr变为[1,4],deleted为[2,3]
slice()
- 参数:
- 第一个参数:起始索引(包含)
- 第二个参数:结束索引(不包含)
- 返回值:新数组
- 特点:不修改原数组,负数索引表示倒数
- 面试题:
- 如何复制一个数组?(
arr.slice()或arr.slice(0)) slice和splice的区别?
- 如何复制一个数组?(
let arr = [1, 2, 3, 4];
let newArr = arr.slice(1, 3); // [2,3],arr不变
1.3 修改数组的方法
reverse()
- 参数:无
- 返回值:反转后的数组
- 特点:直接修改原数组
- 面试题:
reverse会改变原数组吗?(会)- 如何不修改原数组实现反转?(
[...arr].reverse())
let arr = [1, 2, 3];
arr.reverse(); // arr变为[3,2,1]
sort()
- 参数:比较函数(可选)
- 返回值:排序后的数组
- 特点:直接修改原数组,默认按字符串Unicode排序
- 面试题:
- 如何实现数字数组的正确排序?
sort的时间复杂度是多少?(V8引擎使用快速排序和插入排序,O(n log n))
let arr = [10, 5, 20];
arr.sort(); // [10,20,5](按字符串排序)
arr.sort((a,b) => a-b); // [5,10,20](数字升序)
1.4 查找元素的方法
indexOf() vs lastIndexOf()
- 共同参数:要查找的元素,起始位置(可选)
- 区别:
indexOf从前往后找lastIndexOf从后往前找
- 面试题:
- 如何判断元素是否存在?(
indexOf !== -1或includes) - 两者性能有区别吗?(大数据量下
indexOf可能更快)
- 如何判断元素是否存在?(
let arr = [1, 2, 3, 2];
arr.indexOf(2); // 1
arr.lastIndexOf(2); // 3
includes()
- 参数:要查找的值,起始位置(可选)
- 返回值:布尔值
- 特点:可以识别NaN(
indexOf不能) - 面试题:
includes能查找NaN吗?(可以)includes和indexOf如何选择?(只需要布尔值时用includes)
let arr = [1, 2, NaN];
arr.includes(NaN); // true
arr.indexOf(NaN); // -1
find()
- 参数:回调函数
- 返回值:第一个满足条件的元素,没有则返回undefined
- 特点:可以查找复杂对象
- 面试题:
find和filter的区别?(find返回第一个元素,filter返回所有)- 如何用
find查找对象数组?
let users = [{id:1,name:'A'}, {id:2,name:'B'}];
users.find(u => u.id === 2); // {id:2,name:'B'}
1.5 遍历数组的方法
forEach() vs map()
- 共同点:都遍历数组
- 区别:
map返回新数组,forEach不返回map适合需要返回值的场景,forEach适合只执行操作的场景
- 面试题:
- 什么情况下用
map而不是forEach?(需要新数组时) - 如何提前终止
forEach?(无法直接终止,可用some/every代替)
- 什么情况下用
let arr = [1, 2, 3];
arr.forEach(x => console.log(x)); // 无返回值
let doubled = arr.map(x => x * 2); // [2,4,6]
filter()
- 参数:回调函数
- 返回值:新数组
- 特点:不修改原数组
- 面试题:
filter和find的区别?(filter返回所有匹配,find返回第一个)- 如何用
filter去重?
let arr = [1, 2, 3, 2];
let unique = arr.filter((x,i) => arr.indexOf(x) === i); // [1,2,3]
reduce()
- 参数:
- 回调函数(accumulator, currentValue, index, array)
- 初始值(可选)
- 返回值:累加结果
- 面试题:
- 如何用
reduce实现map功能? reduce的初始值不传会怎样?(使用数组第一个元素作为初始值)
- 如何用
let arr = [1, 2, 3];
let sum = arr.reduce((acc, cur) => acc + cur, 0); // 6
let mapResult = arr.reduce((acc, cur) => [...acc, cur*2], []); // [2,4,6]
1.6 数组转换方法
join()
- 参数:分隔符(默认逗号)
- 返回值:字符串
- 面试题:
- 如何将数组转为以竖线分隔的字符串?(
arr.join('|')) join和toString的区别?(toString固定使用逗号分隔)
- 如何将数组转为以竖线分隔的字符串?(
let arr = [1, 2, 3];
arr.join(); // "1,2,3"
arr.join(''); // "123"
2. JavaScript字符串方法深度解析
2.1 增加内容的方法
concat()
- 参数:多个字符串
- 返回值:新字符串
- 特点:不修改原字符串,性能不如
+操作符 - 面试题:
- 为什么推荐使用
+而不是concat?(+性能更好) concat能连接多个字符串吗?(可以)
- 为什么推荐使用
let str = 'Hello';
str.concat(' ', 'World'); // "Hello World"
padStart() vs padEnd()
- 共同参数:
- 目标长度
- 填充字符串(默认空格)
- 区别:
padStart在前面填充padEnd在后面填充
- 面试题:
- 如何用
padStart实现数字前补零? - 填充字符串会截断吗?(会,确保总长度不超过目标长度)
- 如何用
'5'.padStart(2, '0'); // "05"
'1'.padEnd(3, '0'); // "100"
2.2 删除内容的方法
slice() vs substring() vs substr()
- 共同点:都提取子字符串
- 区别:
slice:参数(start,end),接受负数substring:参数(start,end),负数视为0,自动交换参数substr:参数(start,length),第一个参数接受负数
- 面试题:
- 三个方法的参数区别?
- 哪个方法被标记为废弃?(
substr)
let str = 'Hello';
str.slice(1,3); // "el"
str.substring(1,3); // "el"
str.substr(1,3); // "ell"
2.3 修改字符串的方法
replace()
- 参数:
- 查找值(字符串或正则)
- 替换值(字符串或函数)
- 返回值:新字符串
- 面试题:
- 如何替换所有匹配项?(使用正则加
g标志) - 替换函数参数有哪些?
- 如何替换所有匹配项?(使用正则加
'Hello World'.replace('World', 'JS'); // "Hello JS"
'abab'.replace(/a/g, 'c'); // "cbcb"
trim()系列
- 方法:
trim():去除两端空白trimStart()/trimLeft():去除开头空白trimEnd()/trimRight():去除结尾空白
- 面试题:
trimLeft和trimStart的区别?(别名,功能相同)- 如何自定义去除字符?(可用
replace)
' Hello '.trim(); // "Hello"
' Hello '.trimStart(); // "Hello "
2.4 查找内容的方法
includes() vs indexOf()
- 共同点:都可查找子串
- 区别:
includes返回布尔值indexOf返回位置
- 面试题:
- 什么时候用
includes?(只需要知道是否存在时) - 哪个方法性能更好?(简单场景差异不大)
- 什么时候用
'Hello'.includes('ell'); // true
'Hello'.indexOf('ell'); // 1
startsWith() vs endsWith()
- 参数:
- 要查找的字符串
- 起始位置(可选)
- 面试题:
- 如何检查文件扩展名?(
endsWith) - 如何检查URL协议?(
startsWith)
- 如何检查文件扩展名?(
'file.txt'.endsWith('.txt'); // true
'https://...'.startsWith('https'); // true
2.5 转换方法
split()
- 参数:
- 分隔符(字符串或正则)
- 限制数量(可选)
- 返回值:数组
- 面试题:
- 如何将字符串转为字符数组?(
str.split('')) - 如何实现多分隔符分割?(使用正则)
- 如何将字符串转为字符数组?(
'a,b,c'.split(','); // ["a","b","c"]
'a,b.c'.split(/[,.]/); // ["a","b","c"]
charCodeAt()
- 参数:索引
- 返回值:Unicode编码
- 相关方法:
String.fromCharCode():编码转字符
- 面试题:
- 如何获取字符的ASCII码?(
charCodeAt) - 如何判断字符是大写字母?(
charCodeAt(0) >= 65 && <= 90)
- 如何获取字符的ASCII码?(
'A'.charCodeAt(0); // 65
String.fromCharCode(65); // "A"
结语
本文从面试角度深入解析了JavaScript数组和字符串的各种方法,涵盖了参数说明、返回值、使用场景、性能考量以及相似方法之间的区别等关键知识点。在面试中,面试官不仅会考察你是否知道这些方法,更会关注:
- 方法的选择依据(为什么用A方法而不用B方法)
- 对方法细节的掌握(参数处理、边界情况)
- 性能方面的考量(大数据量下的表现)
- 实际应用能力(如何组合使用这些方法解决问题)
建议读者在学习时:
- 多动手实践每个方法的示例代码
- 思考相似方法之间的区别和应用场景
- 关注方法的性能特点
- 尝试用这些方法解决实际问题
掌握这些基础方法不仅能帮助你在面试中脱颖而出,更能提升日常开发效率和代码质量。祝你在前端之路上越走越远!🚀