前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 热点
旧版
首页程序员面试题库字符串原始类型 下标访问字符
JavaScript值与类型

字符串是原始类型,为什么还能用下标取字符?能用下标改吗?

读 str[0] 时,引擎临时把字符串包装成 String 对象,这个对象上有 0、1、length 这些只读属性,所以能按下标读。字符串本身不可变,str[0] = 'H' 不会生效,严格模式下还会抛 TypeError。

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

字符串下标的三个特性

  1. 包装对象str[0] 触发临时的 String 对象,属性访问完就销毁
  2. 只读索引索引属性不可写,赋值在非严格模式静默失败
  3. 码元为单位一个下标对应一个 16 位码元,不一定是用户看到的一个字

越界时下标返回 undefined,而 charAt 返回空字符串。判断「有没有这个字符」时两者写法不一样,混用容易出错。

核心回答

先记住这个答案

字符串是原始值,本身没有属性。但执行 str[0] 时,引擎会临时创建一个 String 包装对象,这个对象上有 0、1、2 这样的索引属性和 length,读到值后包装对象就被丢弃,所以字符串看起来像数组。这些索引属性是只读的,字符串本身也不可变,给下标赋值不会改动原字符串,严格模式下还会抛 TypeError。

  • 下标访问靠临时包装对象,读完就丢
  • 下标拿到的是长度为 1 的字符串,不是字符类型
  • 字符串不可变,改内容只能生成新字符串
  • 下标按 UTF-16 码元算,表情会被拆成两半

读得到,改不了

先看一段代码,把读、越界、赋值、表情这几种情况放在一起。

下标读写对照JavaScript
'use strict'
const s = 'hello'
console.log(s[0], s.at(-1), s.charAt(10), s[10])
console.log(typeof s[0], s.length)
try {
  s[0] = 'H'                         // 严格模式下报错
} catch (e) {
  console.log(e.constructor.name)
}
console.log(s)                       // 原字符串没变
console.log('H' + s.slice(1))        // 只能生成新字符串
console.log('😀'[0] === '\uD83D', [...'😀'].length)
查看输出与解释
h o  undefined
string 5
TypeError
hello
Hello
true 1

第一行里 s.charAt(10) 越界返回空字符串,所以输出里 o 后面有两个空格;s[10] 越界返回 undefined。s.at(-1) 支持负数下标,取最后一个字符,而 s[-1] 只会去找名为 '-1' 的属性,得到 undefined。 typeof s[0] 是 'string'。JavaScript 没有单独的字符类型,取出来的是长度为 1 的字符串。

包装对象是怎么回事

'hello'.toUpperCase() 能调用方法,'hello'[0] 能取字符,背后是同一个机制。访问原始值的属性时,引擎相当于做了一次 new String('hello'),在这个临时对象上查属性或调方法,用完立刻丢弃。现代引擎会优化掉真实的对象创建,但语义上就是这样。

包装对象上的索引属性描述符是 writable: false、configurable: false。非严格模式下给它赋值会静默失败;严格模式下对只读属性赋值会抛 TypeError。就算赋值成功了,改的也是那个临时对象,原来的字符串不会受影响。

所以字符串的所有「修改」方法,replace、toUpperCase、slice、padStart,都是返回新字符串,原字符串不变。

表情和组合字符会被拆开

下标的单位是 UTF-16 码元,不是用户看到的一个字。常用汉字一个码元就够,但 😀 这样的表情要两个码元组成代理对,'😀'[0] 取到的只是前一半 \uD83D,单独显示出来是乱码。

需要按「字」处理时,用 [...str] 或 Array.from(str),它们按码点拆分,表情会被当成一个整体。截断用户昵称、统计评论字数这类场景,直接用下标或 slice 可能把表情切成两半,界面上会出现一个问号方块。

回答前,多想一步

容易答错的地方

以为字符串是字符数组
字符串只是长得像数组,它没有 push、splice 这些方法,也没有字符类型。要用数组方法得先 split('') 或展开成数组,处理完再 join('')。
以为 str[i] = x 能改字符串
非严格模式下这行不报错,很容易以为改成功了。实际上字符串一个字符都没变。要替换某个位置,只能拼一个新字符串,比如 s.slice(0, i) + x + s.slice(i + 1)。
把下标和 charAt 当成完全一样
正常范围内两者结果相同,越界时不同:下标返回 undefined,charAt 返回空字符串。写 if (s[i]) 和 if (s.charAt(i)) 在越界时都是假值,但比较 s[i] === undefined 时就只能用下标。
试着用自己的话回答

面试官还会怎么问?

那给字符串加一个自定义属性会怎样?

s.foo = 1 在非严格模式下不报错,但属性加在了临时包装对象上,下一行读 s.foo 是 undefined。严格模式下给原始值设置属性会直接抛 TypeError。

for...of 和普通 for 循环遍历字符串有什么区别?

普通 for 配合下标按码元遍历,表情会被拆成两次;for...of 用的是字符串的迭代器,按码点遍历,表情只出现一次。遍历含表情的文本应该用 for...of。

怎么按用户看到的字来拆分,比如带肤色的表情?

[...str] 只能处理到码点。带肤色、家庭组合这类表情由多个码点组成,需要用 Intl.Segmenter 按字素簇拆分,它能把一整个组合表情当成一段。

从一道题,走向一组知识

把知识连起来

值与类型

为什么一个表情符号的 length 是 2,家庭表情甚至是 8?

下标和 length 都按 UTF-16 码元计数,表情符号在两边都会多算。

值与类型

两个看起来一模一样的字符串,为什么用 === 比较却不相等?

组合字符让一个看起来的字占多个下标,normalize 能把一部分合并回去。

参考资料

  • JavaScript:值与类型参考资料

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

本题目录
  1. 先记住这个答案
  2. 读得到,改不了
  3. 包装对象是怎么回事
  4. 表情和组合字符会被拆开
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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