面试官:反转字符串你会几种?别只知道 reverse()

7 阅读4分钟

前言:在前端面试中,“反转字符串”是一道出现率极高的“热身题”。
很多候选人觉得这题太简单,随手写一个 split().reverse().join() 就觉得稳了。但实际上,这道看似简单的题目,能从 API 广度、逻辑思维、编程范式、性能意识 四个维度,彻底摸清一个候选人的底色。

今天,我们不仅要写出代码,更要聊聊代码背后的“门道”。

一、 面试官的内心戏

当面试官让你反转一个字符串时,他到底在看什么?

  1. API 熟练度:你是否还在用老旧的写法?是否了解 ES6+ 的新特性?
  2. 逻辑构建能力:如果不让你用内置 API,你能否手写逻辑?
  3. 计算机基础:你是否理解递归?你是否知道递归在 JavaScript 中的潜在风险?

Level 1:API 熟练度流派 (一行代码)

这是最常见的解法,也是实际开发中最常用的。

1.1 基础版

JavaScript

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

1.2 进阶版(加分项)

JavaScript

function reverseStr(str) {
    // 使用 ES6 扩展运算符展开
    return [...str].reverse().join('');
}
console.log(reverseStr('hello')); // olleh

为什么推荐用 [...str]?
面试官可能会追问:“这两个写法有什么区别?”
这时候你可以拿出杀手锏:Emoji 支持

JavaScript 中的字符串是 UTF-16 编码,某些特殊字符(如 Emoji或生僻字)由两个码元(Surrogate Pairs)组成。

  • split('') 会把一个 Emoji 劈成两半,导致乱码。
  • [...str] 使用了字符串迭代器,能够智能识别码点,完整保留 Emoji。

codeJavaScript

const s = "🚀火箭";
console.log(s.split('').reverse().join('')); // "箭火" (乱码)
console.log([...s].reverse().join(''));      // "箭火🚀" (完美)

Level 2:稳健的循环逻辑 (不依赖 API)

如果面试官限制:“不能使用 reverse 方法”,这时候考查的是你的逻辑基本功。

2.1 倒序遍历 (Old School)

这是最稳健、最朴实的写法,思路也比较简单,体现了你对数组/字符串索引的掌控。

JavaScript

function reverseStr(str) {
    let reversed = '';
    // 从最后一个字符开始,向前遍历
    for (let i = str.length - 1; i >= 0; i--) {
        reversed += str[i];
    }
    return reversed;
}

2.2 正序遍历 + 头插法 (Modern)

利用 for...of 语法,代码更优雅。核心在于拼接顺序:将新遍历到的字符,拼接到结果字符串的最前面

JavaScript

function reverseStr(str) {
    let reversed = '';
    for (const char of str) {
        // 注意顺序:新字符 + 旧结果
        reversed = char + reversed;
    }
    return reversed;
}

Level 3:函数式编程流派 (Reduce)

如果你想展示自己对 Functional Programming (函数式编程)  的理解,可以用 reduce。

逻辑是累加器与当前值的操作。

JavaScript

function reverseStr(str) {
    // 1. 转为数组
    // 2. 利用 reduce 归并
    return [...str].reduce((acc, cur) => {
        // 核心:把当前字符(cur) 加在 累加器(acc) 的前面
        return cur + acc;
    }, '');
}

解析:

  • acc (accumulator):累加器,保存之前拼接好的反转字符串。
  • cur (current):当前遍历到的字符。
  • cur + acc:实现了“头插法”的效果。

Level 4:递归的艺术与陷阱 (深度拷问)

这是区分“初级工程师”与“资深工程师”的分水岭。写出递归代码只是第一步,指出递归的缺陷才是王者操作。

4.1 递归逻辑

递归的核心在于:把“反转整个字符串”这个大问题,拆解为“反转除去第一个字符后的子串 + 第一个字符”这个小问题。

JavaScript

function reverseStr(str) {
    // 1. 基线条件 (Base Case):字符串为空,停止递归
    if (str === '') {
        return '';
    }
    
    // 2. 递归步骤:
    // substr(1): 截取从索引1到结尾的子串
    // charAt(0): 获取第一个字符
    // 逻辑:把第一个字符扔到最后去
    return reverseStr(str.substr(1)) + str.charAt(0);
}
console.log(reverseStr('hello')); 
// 执行过程:
// reverseStr('ello') + 'h'
// (reverseStr('llo') + 'e') + 'h'
// ...

4.2 为什么工程中慎用?(亮点分析)

如果你写完上面的代码,并主动补充以下分析,面试官会对你刮目相看:

  1. Stack Overflow (爆栈) 风险
    JavaScript 引擎(特别是 V8)在大部分场景下并不支持尾调用优化 (Tail Call Optimization) 。每递归一次,都会在调用栈中创建一个新的栈帧。如果字符串非常长(例如几万个字符),直接就会抛出 RangeError: Maximum call stack size exceeded。
  2. 内存开销大
    在 reverseStr(str.substr(1)) 过程中,JS 需要不断地开辟堆内存来创建新的子字符串(String 是不可变的),这会造成大量的内存分配和垃圾回收(GC)压力。

结论:递归写法虽然体现了“分而治之”的算法思想,但在处理字符串反转这类简单任务时,在 JS 中由于缺乏引擎优化,并不是性能最优解。

总结

一道简单的面试题,可以有四种层级的回答:

方案关键词适用场景备注
Level 1[...str].reverse()日常开发首选,简洁且支持 Emoji
Level 2for...of面试/手写逻辑性能好,逻辑清晰
Level 3reduce函数式编程展示对高阶函数的理解
Level 4递归算法展示慎用,有爆栈风险,适合理论分析

最后建议
在面试时,建议先写出 Level 1(展示 API 广度)或 Level 2(展示逻辑),然后主动提及 Level 4 的递归思路及其缺陷。这种“知其然,更知其所以然”的态度,才是面试官最想要的。