面试必刷:字符串反转的 5 种 JavaScript 解法与深度解析

160 阅读6分钟

在前端面试的基础算法题库中,“反转字符串” 绝对是一道 “看似简单,实则暗藏考点” 的经典题目。它不像复杂算法那样需要高深的数学功底,却能从多个维度全方位考察开发者的技术实力 —— 无论是对 JavaScript 内置 API 的熟悉程度、ES6 新特性的实际运用,还是对算法逻辑的设计能力、内存与性能的优化意识,甚至是编程范式的理解深度,都能通过不同的解法清晰呈现。

这道题的魅力在于,没有绝对 “唯一” 的最优解,不同实现方案背后对应着不同的技术思路:既可以用一行代码快速实现核心功能,展现高效开发能力;也可以手动拆解逻辑,体现对底层原理的掌握;还能通过递归、函数式编程等方式,彰显编程思维的广度与深度。本文将带你全面梳理字符串反转的多种 JavaScript 实现方案,深入剖析每种解法的核心逻辑、适用场景与优劣差异,帮你在面试中从容应对这道 “基础题”,展现超出预期的技术功底。

split + reverse + join 经典组合

这是最广为人知的实现方式,核心思路是将字符串拆分为字符数组,反转数组后再拼接回字符串:

function reverseStr(str) {
    // 边界处理:空字符串直接返回
    if (!str) return str;
    // split(''):将字符串拆分为字符数组(如"abc"→["a","b","c"])
    // reverse():反转数组(["a","b","c"]→["c","b","a"])
    // join(''):将数组拼接为字符串(["c","b","a"]→"cba")
    return str.split('').reverse().join('');
}

console.log(reverseStr('liujingmiao')); // 输出:"oaimgniujil"

image.png

该解法的优势在于简洁直观,一行代码即可完成核心逻辑,时间复杂度为 O (n)(split、reverse、join 均为线性操作)

空间复杂度为 O (n)(需额外存储字符数组)。

扩展运算符(...)

ES6 的扩展运算符提供了更简洁的字符串转数组方式,无需调用 split 方法,代码更加精炼:

function reverseStr(str) {
    return [...str].reverse().join('');
}

image.png

扩展运算符天然支持 Unicode 字符的正确拆分,解决了 split ('') 的编码兼容问题,是更现代的 API 用法。这种写法不仅体现了对 ES6 特性的熟悉度,也展现了对边界场景的考量,在面试中会给面试官留下更好的印象。

HashMap

function twoSum(nums,target) {
    // es6 提供了hashMap 很重要
    // 语言提供了一些内置的数据结构
    const diffs = new Map();        // es5 没有HashMap O(1)的时间复杂度
    const len = nums.length;//缓存数组的长度
    for(let i = 0;i<len;i++){
        const complement = target - nums[i];//求和问题变成求差问题
        if(diffs.has(complement))
            return [diffs.get(complement),i];
    }
    //diffs[nums[i]] = i;
    diffs.set(nums[i],i);
}

递归调用

递归解法通过 “大问题拆解为小问题” 的思路实现反转,能考察开发者对递归思想、终止条件设计的掌握,是面试中的 “加分项”,但也存在潜在风险。

// 递归
// 函数自己调用自己
//递归会把大的问题交给小(类似)的问题,整个字符串反转

function reverseStr(str){
    // 退出条件
    if(str === ""){
        return "";
    } else {
        return reverseStr(str.substr(1)) + str.charAt(0);
    }
}

console.log(reverseStr("hello world"));

image.png

核心逻辑:将字符串反转拆解为 “最后一个字符 + 剩余字符串反转”,直到字符串为空时终止递归:

递归解法的优点是逻辑优雅,无需循环即可实现,但存在明显局限性:

  1. 爆栈风险:JavaScript 引擎的调用栈深度有限(通常为 1000-10000 层),若输入字符串过长(如 10000 个字符),会触发栈溢出错误(Maximum call stack size exceeded);
  2. 内存开销:每次递归都会创建新的子串和函数调用栈帧,内存消耗高于迭代法。

因此,递归解法更适用于理解算法思想,实际开发中需谨慎使用,避免处理长字符串。

for of

function reverseStr(str) {
    // 1. 初始化一个空字符串,用于存放反转后的结果
    let reversed = '';

    // 2. 遍历输入字符串 `str` 中的每一个字符
    for (const char of str) {
        
        // 3. 将当前遍历到的字符 `char` 添加到 `reversed` 字符串的**最前面**
        reversed = char + reversed;
    }

    // 4. 返回最终反转后的字符串
    return reversed;
}

console.log(reverseStr("hello world")); // 输出:"dlrow olleh"

image.png 这个方法的核心思想可以概括为: “逐个取出,倒序拼接”

  1. 初始化一个空容器:我们创建一个空字符串 reversed,把它想象成一个准备接收反转后字符的 “篮子”。
  2. 遍历原始字符串:我们从头到尾遍历输入的字符串 str。在每次循环中,我们会得到字符串中的一个字符(char)。
  3. 进行 “插队” 操作:这是最关键的一步。我们不是把新字符 char 追加到 reversed 字符串的末尾(这会得到与原字符串相同的顺序),而是把它放到 reversed 字符串的最前面
  4. 循环直至完成:当循环结束时,原始字符串的所有字符都被我们 “倒着” 放进了 reversed 这个 “篮子” 里,此时 reversed 就是我们想要的结果。

reduce

reduce()  方法对数组中的每个元素按序执行一个提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。 MDN文档是这样写的: image.png

const arr = [1,2,3,4,5,6];
// reducer 函数
// acc 之前的计算结果
// cur 是当前的这一项
const total = arr.reduce((acc,cur)=>{
    console.log(acc,cur);
    return acc + cur;
},0);   //initialValue
console.log(total);
function reverseStr(str){
    // str 字符数组?
    //reduce 数组推平为一个字符
    return [...str].reduce((reversed,char) => char + reversed,'')
}
console.log(reverseStr('hello'));

总结

字符串反转作为前端面试的 “常青树”,看似简单却能全方位折射出开发者的技术功底 —— 从对 JavaScript API 的熟练度、ES6 特性的掌握,到算法逻辑设计、编程范式理解,甚至内存优化意识,都能通过不同解法得到检验。

在实际面试中,单一解法不足以凸显优势,更优的策略是:先给出简洁的 API 实现保证正确性,再补充迭代或递归方案展现逻辑深度,最后分析不同解法的时间 / 空间复杂度、适用场景与潜在风险。这种 “多解法 + 理性分析” 的思路,既能满足面试官对基础能力的考察,又能展现自己的技术视野与优化意识。

归根结底,字符串反转的核心逻辑始终是 “倒序重组字符”,但不同实现路径背后,是对编程语言特性、算法思想、工程实践的综合考量。掌握这些解法不仅能轻松应对面试,更能培养 “一题多解” 的思维习惯,为解决更复杂的算法问题、工程难题打下坚实基础。