前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库String slice substring substr 区别
JavaScript值与类型

slice、substring、substr 遇到负数和起止颠倒时有什么区别?

三个方法在参数正常时结果相同。遇到负数,slice 从末尾倒数,substring 当成 0;起止颠倒时,slice 返回空串,substring 会自动交换。substr 的第二个参数是长度而不是结束位置,已经是遗留 API,新代码统一用 slice。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#值与类型
先看核心答案读代码示例
理解线索

三个截取方法的参数规则

  1. slice(start, end)负数加上字符串长度,从末尾倒数;start 大于 end 时返回空串
  2. substring(start, end)负数和 NaN 当成 0,start 大于 end 时先交换再截取
  3. substr(start, length)start 可以是负数,第二个参数是截取长度,属于规范附录 B 的遗留功能

三个方法都不会修改原字符串,字符串本身是不可变的。它们返回的都是新字符串,所以写 s.slice(1) 却不接返回值,等于什么都没做。

核心回答

先记住这个答案

三个方法都用来截取子串,参数正常时结果一样。区别只在边界:slice 允许负数,负数表示从末尾倒数,start 比 end 大就返回空串;substring 不认负数,负数和 NaN 都当成 0,而且 start 比 end 大时会自动交换两个参数;substr 的第二个参数是要截取的长度,不是结束下标,它被放在规范的附录 B 里,属于为了兼容老代码才保留的功能。日常开发统一用 slice,规则最直观,也和数组的 slice 保持一致。

  • slice 负数从末尾倒数,起止颠倒返回空串
  • substring 负索引按零处理
  • substr 第二参数是长度,已不推荐
  • 日常截取优先用 slice

同一组参数,三种结果

把三个方法放在一起喂同样的参数,差别一眼就能看出来。

对照负数、颠倒和长度JavaScript
const s = 'JavaScript'
const show = (...args) => console.log(args.map((v) => JSON.stringify(v)).join(' '))
// 正常顺序:三者结果一样
show(s.slice(0, 4), s.substring(0, 4), s.substr(0, 4))
// 负数开头:slice 和 substr 从末尾倒数,substring 当成 0
show(s.slice(-6), s.substring(-6), s.substr(-6))
// 负数结尾:只有 slice 会倒数
show(s.slice(0, -6), s.substring(0, -6), s.substr(0, -6))
// 起止颠倒:slice 返回空串,substring 自动交换
show(s.slice(4, 0), s.substring(4, 0))
// substr 的第二个参数是长度
show(s.slice(4, 6), s.substr(4, 6))
查看输出与解释
"Java" "Java" "Java"
"Script" "JavaScript" "Script"
"Java" "" ""
"" "Java"
"Sc" "Script"

第三行最能说明问题。s.slice(0, -6) 把 -6 换算成 10 - 6 = 4,截到下标 4 之前,得到 "Java"。substring(0, -6) 把 -6 当成 0,等于 substring(0, 0),结果是空串。substr(0, -6) 的第二个参数是长度,长度为负就按 0 处理,也是空串。

为什么推荐 slice

substring 的自动交换看起来贴心,其实会掩盖 bug。比如你算出来的结束位置因为逻辑错误比开始位置还小,slice 会老老实实返回空串,你很快就能发现不对;substring 却悄悄把两个数对调,截出一段看似正常的内容,问题就藏起来了。

slice 的负数规则也更好用。取文件扩展名、去掉末尾几个字符这类需求,name.slice(-3)、str.slice(0, -1) 一眼就能看懂,换成 substring 就得先写 str.length - 1。

substr 的问题在于它是唯一一个「第二个参数表示长度」的方法,和数组、和另外两个字符串方法都不一样,读代码时很容易看错。MDN 已经把它标为 deprecated,ESLint 也有规则提示替换。老代码里的 s.substr(start, len) 可以直接改成 s.slice(start, start + len),前提是 start 不是负数。

回答前,多想一步

容易答错的地方

substring 也支持负数倒数
不支持。substring 会把负数和 NaN 都当成 0,想从末尾倒数只能用 slice,或者先用 length 算出正数下标。
slice 起止颠倒会自动交换
会自动交换的是 substring。slice(4, 0) 返回空串,不会帮你调整参数顺序。
截取方法会修改原字符串
字符串是原始值,不可变。三个方法都返回新字符串,原来的 s 不受影响。和数组的 splice 混在一起记的人最容易在这里答错。
试着用自己的话回答

面试官还会怎么问?

字符串里有 emoji,slice 会不会截坏?

会。三个方法都按 UTF-16 码元计数,一个 emoji 通常占两个码元,截在中间就会得到半个字符,显示成乱码。要按字符截取,先用 Array.from(str) 或 [...str] 拆成字符数组,再对数组 slice,最后 join('')。

数组的 slice 和字符串的 slice 规则一样吗?

一样。都是左闭右开,负数从末尾倒数,start 大于等于 end 返回空结果,都不修改原值。这也是推荐字符串统一用 slice 的原因之一,两套规则只用记一套。

取字符串最后一个字符有几种写法?

s.slice(-1)、s.at(-1)、s[s.length - 1] 都可以。at 是 ES2022 加的方法,支持负数下标,空串时返回 undefined;slice(-1) 在空串时返回空串。按需要的返回值选一个就行。

从一道题,走向一组知识

把知识连起来

值与类型

String 的 valueOf 和 toString 有区别吗?为什么对 new String 调用会拿到原始字符串?

包装对象调用 slice 时拿到的是内部的原始字符串,valueOf 讲的就是这个内部值怎么取出来。

值与类型

用 Symbol 当对象的键,和字符串键有什么不同?哪些 API 会忽略它?

slice() 截出的字符串可以当对象属性键;若需要避免同名键冲突,可再比较 Symbol 键。

参考资料

  • JavaScript:值与类型参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 同一组参数,三种结果
  3. 为什么推荐 slice
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑