Skip to content
This page has been auto-translated and may contain errors.View in English

字符串

docs.scrimba.com

你正在为登录的用户构建一个问候语,用户的名字保存在一个变量中。你需要页面显示"欢迎回来,小红",而不是手动输入"小红",因为明天来的是小明,后天来的是你从未听说过的人。能够动态构建、测量和重塑的文本就是字符串,本章讲的就是如何使用它们。

创建字符串

一个**字符串**就是文本:任何用引号括起来的字符序列。你可以使用单引号或双引号,它们的行为完全相同,所以选择一种并保持一致。

js
const firstName = "小红";
const greeting = "欢迎回来";

构建字符串最有用的方法是模板字面量,用反引号而不是引号编写。在模板字面量中,你可以用 ${...} 在文本中插入一个值,JavaScript 会填入该值所代表的内容:

js
const firstName = "小红";
const message = `欢迎回来,${firstName}`;
console.log(message); // 欢迎回来,小红

那个 ${firstName} 是一个槽。当字符串被构建时,槽会被 firstName 的值替换。每当你从多个部分构建文本时,就应该使用模板字面量。

一个**字符串**是由文本组成的值。单引号和双引号会产生相同的字符串,所以选择哪个取决于风格和你需要包含的引号字符:"it's here" 避免了转义,'she said "hi"' 也是。

首选的形式是模板字面量,一个用反引号编写的字符串。它可以做两件普通引号做不了的事。它用 ${...} 进行插值,将任何表达式的结果直接放入文本中,而且它可以跨越多行而不需要特殊的转义:

js
const firstName = "小红";
const itemCount = 3;
const message = `欢迎回来,${firstName}。你有 ${itemCount} 个项目等待。`;
console.log(message); // 欢迎回来,小红。你有 3 个项目等待。

任何能产生值的东西都可以放在槽中,不仅仅是变量名,所以 ${itemCount} 也可以是 ${itemCount + 1}。用 ${...} 构建文本的可读性比用引号和加号胶合片段要好得多,这就是为什么它是默认做法。

字符串是 JavaScript 的文本**基本类型**:语言的基本值类型之一,旁边还有数字和布尔值(参见数据类型了解完整集合)。单引号和双引号可以互换,产生完全相同的值;它们强制的唯一真实决定就是哪个引号字符你不必转义。

值得默认使用的形式是模板字面量,由反引号分隔。它用 ${...} 插值任何表达式,计算内部内容并将结果强制转换为字符串,它保留字面换行符,所以多行文本不需要 \n

js
const firstName = "小红";
const itemCount = 3;
const summary = `嗨 ${firstName},你有 ${itemCount} 个项目,${itemCount === 1 ? "它在" : "它们在"} 等待。`;
console.log(summary); // 嗨 小红,你有 3 个项目,它们在 等待。

槽保存的是一个表达式,不仅仅是一个名字,所以算术、方法调用和比较都可以在其中工作。这种表现力就是为什么插值比连接胜出,至少在两部分以上的字符串中:最终文本的形状在源代码中保持可读。要养成的一个习惯是默认使用反引号,只有在处理一个固定的单片字符串时才使用普通引号,此时插值对你没有任何帮助。

Juno创建字符串 字符串是引号中的文本,单引号和双引号的行为完全相同,所以选择一种并保持一致。要从多个部分构建文本,使用模板字面量:反引号加上 ${...} 槽,用你的值填充。这是你最常用的工具。
Juno创建字符串 单引号和双引号产生相同的字符串,所以根据你需要包含的引号选择。但默认使用模板字面量:反引号让你用 ${...} 插入任何值,并且可以免费跨越多行。一旦进行插值,手动胶合字符串就不值得了。
Juno创建字符串 引号可以互换;唯一选择一个而不是另一个的原因是你不得不转义的字符。默认使用反引号,因为 ${...} 接受一个完整表达式,不仅仅是一个名字,所以算术和比较可以直接在文本中工作。对于固定的单片字符串保留普通引号,其中插值没有任何好处。

连接和测量

你可以用 + 运算符连接两个字符串。这叫做**连接**:

js
const firstName = "小明";
const greeting = "你好 " + firstName;
console.log(greeting); // 你好 小明

注意 '你好 ' 中的空格。连接会精确地胶合字符串,所以如果你忘记了空格,你会得到 你好小明。模板字面量通常对同一项工作读起来更好:`你好 ${firstName}`

每个字符串都知道自己的长度。用 .length 询问它:

js
const firstName = "小明";
console.log(firstName.length); // 2

你也可以使用方括号按位置读取单个字符,从零开始计数:

js
const firstName = "小明";
console.log(firstName[0]); // 小

第一个字符在位置 0,不是 1。这种从零开始的计数在 JavaScript 中随处可见,所以现在开始习惯它是值得的。

两个字符串用 + 连接,这个操作叫做**连接**。它可以工作,但很快就会变得混乱,因为每个字面空格和分隔符都必须在引号内输入:

js
const firstName = "小明";
const lastName = "李";
console.log("你好 " + firstName + " " + lastName); // 你好 小明 李

同一行作为模板字面量是 `你好 ${firstName} ${lastName}`,这就是为什么对于超过一个部分的任何东西,插值都是默认做法。

字符串用 .length 报告其大小,即其字符的计数:

js
const firstName = "小明";
console.log(firstName.length); // 2

你用方括号按索引读取单个字符,从零开始计数,所以 firstName[0] 是第一个字符,firstName[1] 是一个两字符字符串的最后一个:

js
const firstName = "小明";
console.log(firstName[0]); // 小

索引是从零开始的:第一个字符在位置 0。

字符串用 + 连接,这个操作叫做**连接**。对于快速的两部分连接,它很好,但要注意 + 是重载的:字符串在任一侧时它会连接,混入一个数字会将该数字强制转换为字符串,所以 '总计:' + 3 得到 '总计:3'3 + 3 得到 6。模板字面量通过总是产生文本来避免这种歧义,这是超过最简单情况时优先使用它们的另一个原因。

.length 是一个属性,不是方法,所以你读取它时不需要括号:

js
const label = "李";
console.log(label.length); // 1

用方括号索引是从零开始的,返回一个单字符字符串:

js
const label = "李";
console.log(label[0]); // 李

需要记住一件事:.length 计算的是字符串的底层单位,不总是人类所说的字符。对于普通文本,两者是一致的。对于某些emoji和组合字符,它们不一致,因为一个可见符号可能存储为多个单位,所以 '😀'.length 报告 2。它很少会咬你,但当带有emoji的文本的"字母"计数看起来不对时,那个不匹配就是原因。

Juno连接和测量+ 连接字符串,但记得包含你自己的空格,否则 你好 和一个名字会粘在一起。模板字面量通常对同一件事更清晰。使用 .length 计算字符,用方括号如 name[0] 按位置读取一个,从零开始计数。
Juno连接和测量+ 连接,但一旦你有超过两个部分,它就会变得混乱,所以模板字面量对任何真实的东西都是赢家。.length 计算字符,不需要括号因为它是一个属性。用 name[0] 索引是从零开始的,所以第一个字符在位置 0,最后一个在长度减一。
JunoUseful string methods+ 是重载的:字符串加任何东西都会连接并强制转换,这是意外惊喜的安静来源,所以依靠模板字面量。.length 是一个属性,没有括号。它计算存储单位,不总是可见字符,所以一个emoji可以报告长度为 2。那个差距只在unicode中出现,但在它让你困惑之前值得知道。

有用的字符串方法

字符串附带内置的**方法**:你通过写一个点和方法名在字符串上运行的操作。这是你最常用的那些。

toUpperCase()toLowerCase() 改变大小写:

js
const firstName = "xiaohong";
console.log(firstName.toUpperCase()); // XIAOHONG

includes() 检查一段文本是否出现在另一段中,它用 truefalse 回答:

js
const message = "欢迎回来";
console.log(message.includes("回来")); // true

slice() 切出字符串的一部分,给它起始位置和停止位置。用 trim() 清理末尾杂散的空格。用 replace() 交换一段文本为另一段:

js
const raw = "  你好  ";
console.log(raw.trim()); // 你好

const message = "你好 小红";
console.log(message.replace("小红", "小明")); // 你好 小明

还有很多字符串方法,你不需要记住它们。学会这五个,当工作需要时查阅其余的。

字符串携带**方法**,你用一个点在字符串上调用的函数,少数几个覆盖了大多数日常工作。

toUpperCase()toLowerCase() 改变大小写,这对比较用户输入的文本而不担心大小写很方便。includes() 返回一个布尔值,表示一个字符串是否出现在另一个中:

js
const email = "[email protected]";
console.log(email.toLowerCase().includes("@site.com")); // true

slice(start, end) 提取一个部分,从起始索引直到但不包括结束索引。trim() 移除两端的空格,你在几乎任何用户输入上都想要这个。replace() 交换一段文本的第一个匹配为另一段:

js
const raw = "  订单-2024  ";
const clean = raw.trim(); // '订单-2024'
console.log(clean.slice(0, 2)); // 订单
console.log(clean.replace("订单", "发票")); // 发票-2024

这五个是主力。很多其他存在,所以当你遇到它们无法覆盖的东西时,查阅参考,但预期依靠这些。

字符串公开了一个大型的**方法**集合,用点在值上调用的函数。一个小核心承载了大部分工作:toUpperCase() / toLowerCase() 用于大小写,includes() 用于布尔子字符串检查,slice(start, end) 用于提取部分(开始包含,结束排除,负数从末尾计数),trim() 用于剥离周围空格,replace() 用于替换。

将它们联系在一起的属性:字符串方法永远不改变原始字符串,它们返回一个新字符串。字符串是不可变的,意味着字符串值不能被原位改变,所以每个方法都交给你一个新字符串,原始字符串保持不动:

js
const raw = "  小红 李  ";
const cleaned = raw.trim().toLowerCase();
console.log(cleaned); // 小红 李
console.log(raw); // '  小红 李  '  (未改变)

因为结果本身是一个字符串,方法链,如上面的 trim().toLowerCase() 所示。两个值得记住的边界情况:replace() 仅交换第一个匹配,除非你要求所有的,而且超过末尾索引返回 undefined 而不是错误,所以 raw[999]undefined,不是崩溃。那个无声的 undefined 就是那种稍后表现为困惑的结果而不是清晰的失败的东西。

Juno有用的字符串方法 方法是你用一个点在字符串上运行的操作:toUpperCase()includes() 用于是或否检查、slice() 用于切出一部分、trim() 用于移除杂散空格、replace() 用于交换文本。还有很多,所以学会这五个,当你需要时查阅其余的。
Juno有用的字符串方法 依靠五个:toUpperCase()toLowerCase() 用于大小写,includes() 用于布尔检查,slice(start, end) 用于提取(结束不包括),trim() 用于用户输入,replace() 用于第一个匹配。很多其他存在,所以当这些不足时查阅参考,但这些覆盖了大多数日子。
Juno有用的字符串方法 核心五个是 toUpperCase/toLowerCaseincludesslicetrimreplace,它们都返回一个新字符串因为字符串是不可变的,所以结果链接而原始永远不改变。两个陷阱:replace 默认仅命中第一个匹配,超过末尾索引给出 undefined 而不是错误,这往往表现为一个令人困惑的结果,而不是一个清晰的崩溃。

字符串接下来去哪里

字符串是 JavaScript 的核心构建块之一,你现在拥有重要的部分:用引号或模板字面量创建它们,连接和测量它们,以及用总是返回新东西的方法重塑它们。你在这里使用的同一点和方法模式会再次出现在其他类型上,最值得注意的是当你开始在数组方法中使用列表时。

文本也很少单独旅行。一旦你显示计数、价格或总计,你就在混合字符串和数字,模板字面量就是你如何干净地将两者缝合在一起的方式。