Langchain系列文章目录
01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来
Python系列文章目录
PyTorch系列文章目录
机器学习系列文章目录
深度学习系列文章目录
Java系列文章目录
JavaScript系列文章目录
01-【JavaScript-Day 1】从零开始:全面了解 JavaScript 是什么、为什么学以及它与 Java 的区别
02-【JavaScript-Day 2】开启 JS 之旅:从浏览器控制台到 <script>
标签的 Hello World 实践
03-【JavaScript-Day 3】掌握JS语法规则:语句、分号、注释与大小写敏感详解
04-【JavaScript-Day 4】var
完全指南:掌握变量声明、作用域及提升
05-【JavaScript-Day 5】告别 var
陷阱:深入理解 let
和 const
的妙用
06-【JavaScript-Day 6】从零到精通:JavaScript 原始类型 String, Number, Boolean, Null, Undefined, Symbol, BigInt 详解
07-【JavaScript-Day 7】全面解析 Number 与 String:JS 数据核心操作指南
文章目录
- Langchain系列文章目录
- Python系列文章目录
- PyTorch系列文章目录
- 机器学习系列文章目录
- 深度学习系列文章目录
- Java系列文章目录
- JavaScript系列文章目录
- 前言
- 一、数字 (Number) 类型详解
- 二、字符串 (String) 类型详解
- 三、数字与字符串的交互
- 四、实际应用场景示例
- 五、总结
前言
欢迎来到 JavaScript 学习系列的第七篇!在上一篇文章中,我们初步认识了 JavaScript 中的七种原始数据类型,它们是构建程序逻辑的基石。今天,我们将深入探索其中两个最为常用也最为核心的类型:数字 (Number
) 和字符串 (String
)。无论您是编程新手还是希望巩固基础的进阶者,理解并熟练运用这两种类型都至关重要。本文将从基础概念入手,逐步深入到核心方法和实际应用场景,帮助您全面掌握它们在 JavaScript 世界中的奥秘。
一、数字 (Number) 类型详解
在 JavaScript 中,数字类型 (Number
) 用于表示整数和浮点数。与其他一些编程语言不同,JavaScript 不区分整数类型和浮点数类型,所有数字都统一为 Number
类型,采用 IEEE 754 标准的双精度 64 位格式表示。
1.1 数字类型的基本认知
1.1.1 什么是 Number 类型?
Number
类型是 JavaScript 中的一种原始数据类型,用于表示数值。它可以是整数,如 10
, -5
, 0
;也可以是浮点数(小数),如 3.14
, -0.5
, 2.5e4
(科学计数法,表示
2.5
×
1
0
4
2.5 \times 10^4
2.5×104)。
let integerNumber = 100;
let floatNumber = 3.14159;
let scientificNotation = 2.998e8; // 光速,约等于 299,800,000
console.log(typeof integerNumber); // 输出: "number"
console.log(typeof floatNumber); // 输出: "number"
console.log(scientificNotation); // 输出: 299800000
1.1.2 JavaScript 中数字的表示方式
JavaScript 中的数字主要以十进制表示。虽然规范也支持二进制、八进制和十六进制的字面量表示,但在日常开发中,十进制最为常见。
- 十进制 (Decimal): 我们最常用的形式,如
123
,0.5
。 - 二进制 (Binary): 以
0b
或0B
开头,如0b101
(十进制的 5)。 - 八进制 (Octal): 在严格模式下,旧的以
0
开头的八进制表示法(如012
)会被视为十进制或语法错误。ES6 引入了以0o
或0O
开头的表示法,如0o12
(十进制的 10)。 - 十六进制 (Hexadecimal): 以
0x
或0X
开头,如0xFF
(十进制的 255)。
console.log(0b1101); // 输出: 13 (二进制)
console.log(0o755); // 输出: 493 (八进制)
console.log(0x1A); // 输出: 26 (十六进制)
1.1.3 浮点数的精度问题
由于计算机使用二进制表示浮点数,某些十进制小数无法被精确表示,这可能导致微小的计算误差。这是基于 IEEE 754 标准的浮点数运算的普遍现象,并非 JavaScript 所独有。
console.log(0.1 + 0.2); // 输出: 0.30000000000000004 (而不是精确的 0.3)
console.log(0.1 + 0.2 === 0.3); // 输出: false
场景驱动: 在进行金融计算或需要高精度比较的场景时,需要特别注意这个问题。常用的解决方案包括将浮点数转换为整数进行计算,或者使用专门的数学库(如 decimal.js
)。对于简单的比较,可以设定一个极小的误差范围(epsilon)。
let num1 = 0.1;
let num2 = 0.2;
let sum = num1 + num2;
const epsilon = 1e-9; // 定义一个非常小的数作为误差容忍度
if (Math.abs(sum - 0.3) < epsilon) {
console.log("0.1 + 0.2 近似等于 0.3");
} else {
console.log("0.1 + 0.2 不近似等于 0.3");
}
// 输出: 0.1 + 0.2 近似等于 0.3
1.2 特殊的数值
JavaScript 的 Number
类型还包含一些特殊的数值。
1.2.1 Infinity
与 -Infinity
Infinity
是一个特殊的数值,表示正无穷大。当一个数超过了 JavaScript 能表示的最大数值时,就会得到 Infinity
。类似地,-Infinity
表示负无穷大。
console.log(1 / 0); // 输出: Infinity
console.log(-1 / 0); // 输出: -Infinity
console.log(Number.MAX_VALUE * 2); // 输出: Infinity (超过最大值)
console.log(Number.MIN_VALUE / 2); // 输出: 0 (接近0,但仍为正数,除以2不会变成-Infinity)
console.log(-Number.MAX_VALUE * 2); // 输出: -Infinity
Infinity
可以参与运算:
console.log(Infinity + 1); // Infinity
console.log(Infinity * 5); // Infinity
console.log(10 / Infinity); // 0
1.2.2 NaN
(Not-a-Number)
NaN
是一个特殊的数值,表示“非数字”(Not-a-Number)。它通常在数学运算无法返回有效数字时出现。
NaN
的特性:
- 任何涉及
NaN
的算术运算(除了某些位运算)的结果都是NaN
。 NaN
与任何值都不相等,包括它自身 (NaN !== NaN
)。
console.log(0 / 0); // 输出: NaN
console.log("abc" / 3); // 输出: NaN
console.log(Math.sqrt(-1)); // 输出: NaN
console.log(NaN + 5); // 输出: NaN
console.log(NaN === NaN); // 输出: false
如何判断 NaN
?
由于 NaN !== NaN
,不能直接用 ===
来判断一个值是否为 NaN
。应使用 Number.isNaN()
(ES6推荐) 或全局函数 isNaN()
。
Number.isNaN(value)
: 检查一个值是否严格为NaN
。只有当value
的类型是number
且其值为NaN
时才返回true
。isNaN(value)
: 会先尝试将参数转换为数字,如果转换后是NaN
,则返回true
。这可能导致一些意外行为。
console.log(Number.isNaN(NaN)); // 输出: true (推荐)
console.log(Number.isNaN("hello")); // 输出: false (因为 "hello" 不是数字 NaN)
console.log(Number.isNaN(0/0)); // 输出: true
console.log(isNaN(NaN)); // 输出: true
console.log(isNaN("hello")); // 输出: true (因为 "hello" 转换为数字是 NaN)
console.log(isNaN(123)); // 输出: false
场景驱动: 在处理用户输入或外部数据时,经常需要检查转换后的结果是否为有效的数字。使用 Number.isNaN()
是更安全的选择。
1.3 常用的 Number 方法与属性
Number
对象本身(构造函数)和 Number
类型的实例(通过 Number
对象的方法或数字字面量创建的数字)都提供了一些有用的方法和属性。
1.3.1 Number.isFinite()
Number.isFinite(value)
方法用来判断传入的值是否是一个有限的数值。如果参数是 NaN
、正无穷大 (Infinity
) 或负无穷大 (-Infinity
),则返回 false
,否则返回 true
。与全局 isFinite()
不同,它不会对参数进行类型转换。
console.log(Number.isFinite(123)); // 输出: true
console.log(Number.isFinite(0)); // 输出: true
console.log(Number.isFinite(Infinity)); // 输出: false
console.log(Number.isFinite(-Infinity)); // 输出: false
console.log(Number.isFinite(NaN)); // 输出: false
console.log(Number.isFinite("123")); // 输出: false (不会转换类型)
console.log(isFinite("123")); // 输出: true (全局 isFinite 会先尝试转换 "123" 为数字)
1.3.2 Number.isInteger()
Number.isInteger(value)
方法用来判断传入的值是否为整数。
console.log(Number.isInteger(10)); // 输出: true
console.log(Number.isInteger(10.0)); // 输出: true (浮点数表示的整数也被认为是整数)
console.log(Number.isInteger(10.5)); // 输出: false
console.log(Number.isInteger("10")); // 输出: false (不会转换类型)
console.log(Number.isInteger(NaN)); // 输出: false
console.log(Number.isInteger(Infinity)); // 输出: false
1.3.3 Number.isNaN()
前面已经介绍过,这是判断一个值是否为 NaN
的最可靠方法。
1.3.4 num.toFixed(digits)
toFixed()
方法将一个数字转换为字符串,并保留指定小数位数 digits
(0 到 20 之间)。它会进行四舍五入。
let pi = 3.1415926535;
console.log(pi.toFixed(2)); // 输出: "3.14" (四舍五入)
console.log(pi.toFixed(0)); // 输出: "3" (四舍五入到整数)
console.log((1.234).toFixed(2)); // 输出: "1.23"
console.log((1.235).toFixed(2)); // 输出: "1.24" (向上舍入)
console.log((2500).toFixed(2)); // 输出: "2500.00"
let price = 19.9;
// 场景:显示价格,通常保留两位小数
console.log(`价格: ¥${price.toFixed(2)}`); // 输出: 价格: ¥19.90
注意: toFixed()
返回的是字符串类型。如果需要进行后续数学运算,记得转换回数字。
1.3.5 num.toPrecision(precision)
toPrecision()
方法以指定的精度(有效数字的位数,1 到 100 之间)返回该数值对象的字符串表示。如果 precision
参数小于数值的整数位数,则会使用科学计数法。
let num = 123.456;
console.log(num.toPrecision(5)); // 输出: "123.46" (5位有效数字,四舍五入)
console.log(num.toPrecision(3)); // 输出: "123" (3位有效数字,四舍五入)
console.log(num.toPrecision(2)); // 输出: "1.2e+2" (2位有效数字,科学计数法)
console.log((0.0012345).toPrecision(3)); // 输出: "0.00123"
console.log((1234567).toPrecision(3)); // 输出: "1.23e+6"
toPrecision()
同样返回字符串。
1.3.6 parseInt(string, radix)
parseInt()
函数解析一个字符串参数,并返回一个指定基数(radix
)的整数。
string
: 要被解析的值。如果参数不是一个字符串,则会使用ToString
抽象操作将其转换成字符串。字符串开头的空白符将会被忽略。radix
: 一个介于 2 和 36 之间的整数,表示上述字符串的基数。如果radix
未指定或者为 0,则数字将以 10 为基础来解析,除非数字以0x
或0X
开头,那么基数是 16。ES5 之后,如果radix
未指定且字符串不以0x
或0X
开头,则默认为 10。强烈建议总是提供radix
参数,以避免混淆和意外行为。
console.log(parseInt("100")); // 输出: 100 (radix 默认为 10)
console.log(parseInt("100", 10)); // 输出: 100
console.log(parseInt(" 100px", 10)); // 输出: 100 (忽略前导空格,解析到非数字字符为止)
console.log(parseInt("0xFF", 16)); // 输出: 255 (十六进制)
console.log(parseInt("101", 2)); // 输出: 5 (二进制)
console.log(parseInt("hello", 10)); // 输出: NaN (无法解析)
console.log(parseInt("10.5", 10)); // 输出: 10 (只取整数部分)
console.log(parseInt("010")); // 在旧版浏览器或非严格模式下可能被当做八进制,输出 8。
// ES5+ 默认为 10,输出 10。为清晰起见,总是指定radix。
console.log(parseInt("010", 10)); // 输出: 10
console.log(parseInt("010", 8)); // 输出: 8
1.3.7 parseFloat(string)
parseFloat()
函数解析一个字符串参数并返回一个浮点数。它会忽略前导空白,并尽可能多地解析数字,直到遇到无法解析为数字的字符为止。
console.log(parseFloat("3.14")); // 输出: 3.14
console.log(parseFloat(" 3.14159 ")); // 输出: 3.14159 (忽略前后空格)
console.log(parseFloat("3.14 meters")); // 输出: 3.14 (解析到 " meters" 为止)
console.log(parseFloat("Pi is 3.14")); // 输出: NaN (开头不是数字)
console.log(parseFloat("314e-2")); // 输出: 3.14 (支持科学计数法)
console.log(parseFloat("0xFF")); // 输出: 0 (parseFloat 不支持十六进制前缀,它会解析0)
console.log(parseFloat("Infinity")); // 输出: Infinity
parseFloat
总是以十进制解析。
1.3.8 num.toString(radix)
toString()
方法返回指定 Number
对象的字符串表示形式。可以传入一个可选的 radix
参数来指定数字到字符串的转换基数 (2 到 36)。如果未指定 radix
,则默认为 10。
let count = 10;
console.log(count.toString()); // 输出: "10" (radix 默认为 10)
console.log((17).toString(16)); // 输出: "11" (17 的十六进制表示)
console.log((10).toString(2)); // 输出: "1010" (10 的二进制表示)
console.log((3.14).toString()); // 输出: "3.14"
表格:Number 常用方法总结
方法/属性 | 描述 | 返回值类型 | 示例 (num = 123.456 ) |
---|---|---|---|
Number.isFinite(val) | 判断是否为有限数值 | boolean | Number.isFinite(num) (true) |
Number.isInteger(val) | 判断是否为整数 | boolean | Number.isInteger(num) (false) |
Number.isNaN(val) | 判断是否为 NaN (严格) | boolean | Number.isNaN(0/0) (true) |
num.toFixed(digits) | 转换为字符串,保留指定小数位数 (四舍五入) | string | num.toFixed(2) (“123.46”) |
num.toPrecision(prec) | 转换为字符串,保留指定有效数字位数 (四舍五入) | string | num.toPrecision(4) (“123.5”) |
parseInt(str, radix) | 将字符串解析为整数 (建议指定 radix ) | number | parseInt("123px", 10) (123) |
parseFloat(str) | 将字符串解析为浮点数 | number | parseFloat("123.45$") (123.45) |
num.toString(radix) | 将数字转换为指定基数的字符串 (默认基数 10) | string | (255).toString(16) (“ff”) |
二、字符串 (String) 类型详解
字符串 (String
) 类型是 JavaScript 中的一种原始数据类型,用于表示文本数据。它是一系列 16 位无符号整数值的序列,每个值通常表示一个 UTF-16 字符。
2.1 字符串类型的基本认知
2.1.1 什么是 String 类型?
简单来说,字符串就是一串字符。例如,你的名字、一篇文章、网页上的文本内容等都可以用字符串来表示。
2.1.2 创建字符串的方式
在 JavaScript 中,有三种主要方式可以创建字符串:
(1)使用单引号 ('
)
let singleQuotedString = 'Hello, World!';
console.log(singleQuotedString); // 输出: Hello, World!
(2)使用双引号 ("
)
let doubleQuotedString = "JavaScript is fun.";
console.log(doubleQuotedString); // 输出: JavaScript is fun.
选择单引号还是双引号?
在 JavaScript 中,单引号和双引号的功能完全相同。选择哪一种主要取决于个人或团队的编码风格。一个常见的做法是,如果字符串本身包含一种引号,则用另一种引号来包裹它,以避免转义。
let message1 = "He said, 'Hi!'"; // 字符串内有单引号,用双引号包裹
let message2 = 'She replied, "Hello."'; // 字符串内有双引号,用单引号包裹
如果字符串内外使用了同一种引号,则需要使用反斜杠 \
进行转义:
let escapedString = 'It\'s a beautiful day.'; // 转义单引号
console.log(escapedString); // 输出: It's a beautiful day.
(3)使用反引号 (```) (模板字符串/Template Literals)
ES6 引入了模板字符串,它提供了更强大的字符串表示方式,主要有两个优点:
- 支持多行字符串:可以直接在反引号内换行。
- 支持字符串插值 (Interpolation):可以使用
${expression}
语法嵌入变量或表达式。
let name = "Alice";
let age = 30;
// 多行字符串
let multiLineString = `This is a
multi-line
string.`;
console.log(multiLineString);
/*
输出:
This is a
multi-line
string.
*/
// 字符串插值
let greeting = `Hello, my name is ${name} and I am ${age} years old.`;
console.log(greeting); // 输出: Hello, my name is Alice and I am 30 years old.
let price = 10;
let tax = 0.05;
let totalMessage = `The total price is $${(price * (1 + tax)).toFixed(2)}.`;
console.log(totalMessage); // 输出: The total price is $10.50.
模板字符串因其便利性和可读性,在现代 JavaScript 开发中被广泛使用。
2.2 字符串的常用属性与操作
2.2.1 length
属性:获取字符串长度
字符串的 length
属性返回字符串中字符的数量(基于 UTF-16 码元)。
let str = "Hello";
console.log(str.length); // 输出: 5
let emptyStr = "";
console.log(emptyStr.length); // 输出: 0
let complexStr = "你好,世界!"; // 中文字符通常也各占一个码元长度
console.log(complexStr.length); // 输出: 6
注意: 对于一些 Unicode 增补字符(如某些表情符号),它们可能由两个 UTF-16 码元组成,此时 length
属性可能不会直观地反映“字符”数量。
2.2.2 访问字符串中的字符
可以使用方括号表示法 []
配合索引来访问字符串中的特定字符。索引从 0 开始。
let fruit = "Apple";
console.log(fruit[0]); // 输出: "A"
console.log(fruit[1]); // 输出: "p"
console.log(fruit[fruit.length - 1]); // 输出: "e" (最后一个字符)
console.log(fruit[10]); // 输出: undefined (索引超出范围)
重要: JavaScript 中的字符串是不可变的 (immutable)。这意味着一旦字符串被创建,它的内容就不能被改变。所有看起来修改了字符串的方法,实际上都是返回了一个新的字符串。
let s = "hello";
s[0] = "H"; // 尝试修改第一个字符
console.log(s); // 输出: "hello" (字符串没有改变)
s = "world"; // 这是重新赋值,s 指向了一个新的字符串 "world"
console.log(s); // 输出: "world"
2.2.3 字符串拼接
将多个字符串连接成一个更长的字符串。
(1)使用 +
运算符
最常用的字符串拼接方式是使用 +
运算符。如果其中一个操作数是字符串,+
运算符会执行字符串拼接。
let firstName = "John";
let lastName = "Doe";
let fullName = firstName + " " + lastName; // " " 是一个包含空格的字符串
console.log(fullName); // 输出: "John Doe"
let strNum = "Age: " + 30;
console.log(strNum); // 输出: "Age: 30" (数字 30 被隐式转换为字符串 "30")
(2)使用 concat()
方法
String.prototype.concat()
方法用于连接一个或多个字符串到调用它的字符串上,并返回一个新的字符串。
let str1 = "Hello";
let str2 = " ";
let str3 = "World";
let combined = str1.concat(str2, str3, "!");
console.log(combined); // 输出: "Hello World!"
// 与 + 运算符比较
console.log(str1 + str2 + str3 + "!"); // 输出: "Hello World!"
虽然 concat()
也能实现拼接,但 +
运算符通常更简洁易读,性能在现代 JavaScript 引擎中也相当不错。
(3)模板字符串中的插值 ${}
如前所述,模板字符串通过 ${expression}
语法自然地实现了字符串拼接和变量嵌入,可读性非常好。
let item = "Book";
let price = 19.99;
let message = `The ${item} costs $${price}.`;
console.log(message); // 输出: The Book costs $19.99.
2.3 常用的 String 方法
字符串对象提供了大量用于操作和查询字符串内容的方法。记住,这些方法都不会修改原始字符串,而是返回一个新的字符串或结果。
2.3.1 查找与定位
这些方法帮助你找到子字符串在原字符串中的位置或判断是否存在。
(1) indexOf(searchValue, fromIndex)
indexOf()
方法返回 searchValue
在字符串中首次出现的索引,如果未找到则返回 -1
。
searchValue
: 要查找的子字符串。fromIndex
(可选): 开始查找的起始索引,默认为 0。
let text = "Hello world, welcome to the world of JavaScript.";
console.log(text.indexOf("world")); // 输出: 6 (第一个 "world" 的起始位置)
console.log(text.indexOf("world", 10)); // 输出: 29 (从索引 10 开始查找 "world")
console.log(text.indexOf("Python")); // 输出: -1 (未找到)
console.log(text.indexOf("o")); // 输出: 4 (第一个 'o')
(2) lastIndexOf(searchValue, fromIndex)
lastIndexOf()
方法返回 searchValue
在字符串中最后一次出现的索引,如果未找到则返回 -1
。
searchValue
: 要查找的子字符串。fromIndex
(可选): 从字符串的末尾向前开始查找的起始索引。默认为字符串的长度str.length
。
let text = "Hello world, welcome to the world of JavaScript.";
console.log(text.lastIndexOf("world")); // 输出: 29 (最后一个 "world" 的起始位置)
console.log(text.lastIndexOf("world", 20)); // 输出: 6 (从索引 20 向前查找 "world")
console.log(text.lastIndexOf("o")); // 输出: 32 (最后一个 'o')
console.log(text.lastIndexOf("o", 5)); // 输出: 4 (从索引 5 向前查找 'o')
(3) includes(searchValue, position)
(ES6)
includes()
方法判断一个字符串是否包含指定的子字符串,返回 true
或 false
。
searchValue
: 要查找的子字符串。position
(可选): 开始搜索searchValue
的位置,默认为 0。
let sentence = "The quick brown fox jumps over the lazy dog.";
console.log(sentence.includes("fox")); // 输出: true
console.log(sentence.includes("Fox")); // 输出: false (大小写敏感)
console.log(sentence.includes("cat")); // 输出: false
console.log(sentence.includes("quick", 10)); // 输出: false (从索引10开始,"quick"在索引4)
(4) startsWith(searchString, position)
(ES6)
startsWith()
方法用来判断当前字符串是否以另一个给定的子字符串开头,并根据判断结果返回 true
或 false
。
searchString
: 要搜索的子字符串。position
(可选): 在当前字符串中开始搜索searchString
的位置,默认为 0。
let str = "Hello world!";
console.log(str.startsWith("Hello")); // 输出: true
console.log(str.startsWith("world")); // 输出: false
console.log(str.startsWith("world", 6)); // 输出: true (从索引 6 开始,"world!"以"world"开头)
(5) endsWith(searchString, length)
(ES6)
endsWith()
方法用来判断当前字符串是否以另一个给定的子字符串结尾,并根据判断结果返回 true
或 false
。
searchString
: 要搜索的子字符串。length
(可选): 作为原始字符串的长度。默认为原始字符串的length
。它有效地将字符串视为仅有这么多字符。
let filename = "document.pdf";
console.log(filename.endsWith(".pdf")); // 输出: true
console.log(filename.endsWith(".doc")); // 输出: false
console.log(filename.endsWith("document", 8)); // 输出: true ("document.pdf"的前8个字符是"document",它以"document"结尾)
2.3.2 提取子字符串
这些方法用于从一个字符串中提取一部分。
(1) slice(startIndex, endIndex)
slice()
方法提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串。
startIndex
: 开始提取的索引(包含此元素)。如果为负数,则从字符串尾部开始计数,例如-1
指的是最后一个字符。endIndex
(可选): 结束提取的索引(不包含此元素)。如果省略,则提取到字符串末尾。如果为负数,也从字符串尾部开始计数。
let str = "JavaScript";
// J a v a S c r i p t
// 0 1 2 3 4 5 6 7 8 9
//-10-9-8-7-6-5-4-3-2-1 (负数索引示意)
console.log(str.slice(0, 4)); // 输出: "Java" (从索引0到3)
console.log(str.slice(4)); // 输出: "Script" (从索引4到末尾)
console.log(str.slice(-6)); // 输出: "Script" (从倒数第6个字符到末尾)
console.log(str.slice(1, -3)); // 输出: "avaScr" (从索引1到倒数第3个字符之前)
console.log(str.slice(4, 1)); // 输出: "" (如果 startIndex > endIndex,返回空字符串)
(2) substring(startIndex, endIndex)
substring()
方法返回一个字符串在开始索引到结束索引之间的一个子集,或从开始索引直到字符串的末尾的一个子集。
startIndex
: 开始提取的索引(包含)。endIndex
(可选): 结束提取的索引(不包含)。- 如果
startIndex
等于endIndex
,返回一个空字符串。 - 如果
startIndex
大于endIndex
,substring
会交换这两个参数。 - 任何小于 0 或大于
str.length
的参数值都会被规范化(小于0的视为0,大于长度的视为长度)。
let str = "JavaScript";
console.log(str.substring(0, 4)); // 输出: "Java"
console.log(str.substring(4)); // 输出: "Script"
console.log(str.substring(4, 0)); // 输出: "Java" (参数被交换为 substring(0, 4))
console.log(str.substring(-5, 4)); // 输出: "Java" (负数被视为0,即 substring(0, 4))
console.log(str.substring(1, 20)); // 输出: "avaScript" (大于长度的被视为长度,即 substring(1, 10))
slice()
vs substring()
关键区别:
slice()
可以接受负数索引,从字符串尾部计算。substring()
会将负数索引视为 0。- 如果
slice()
的startIndex
大于endIndex
,它会返回空字符串。substring()
会交换这两个参数。
一般来说,slice()
更灵活且行为更符合直觉,尤其是处理负数索引时。
(3) substr(startIndex, length)
(已废弃)
substr()
方法返回一个由 startIndex
开始的指定 length
的字符组成的子字符串。
startIndex
: 开始提取字符的位置。如果为负数,则从字符串尾部开始算起。length
: 要提取的字符数。
注意:substr()
已被标记为废弃 (deprecated),虽然在很多浏览器中仍然可用,但建议使用slice()
或substring()
替代。
let str = "JavaScript";
console.log(str.substr(0, 4)); // 输出: "Java" (从索引0开始,取4个字符)
console.log(str.substr(4, 6)); // 输出: "Script" (从索引4开始,取6个字符)
console.log(str.substr(-6, 6)); // 输出: "Script" (从倒数第6个字符开始,取6个字符)
2.3.3 大小写转换
(1) toUpperCase()
toUpperCase()
方法将调用该方法的字符串值转换为大写形式,并返回。
let greeting = "Hello, World!";
console.log(greeting.toUpperCase()); // 输出: "HELLO, WORLD!"
(2) toLowerCase()
toLowerCase()
方法将调用该方法的字符串值转换为小写形式,并返回。
let greeting = "Hello, World!";
console.log(greeting.toLowerCase()); // 输出: "hello, world!"
场景驱动: 在进行不区分大小写的比较或数据规范化时,通常会将字符串统一转换为大写或小写。
let userInput = " JaVaScRiPt ";
let searchTerm = "javascript";
// 规范化用户输入并比较
if (userInput.trim().toLowerCase() === searchTerm) {
console.log("Input matches the search term.");
} else {
console.log("Input does not match.");
}
// 输出: Input matches the search term.
2.3.4 去除空白
这些方法用于去除字符串开头和/或结尾的空白字符(空格、制表符、换行符等)。
(1) trim()
trim()
方法会从一个字符串的两端删除空白字符。
let messyString = " Hello World ";
console.log(messyString.trim()); // 输出: "Hello World"
(2) trimStart()
/ trimLeft()
(ES2019)
trimStart()
(或其别名 trimLeft()
) 方法从字符串的开头删除空白。
let leadingSpace = " Leading";
console.log(leadingSpace.trimStart()); // 输出: "Leading"
console.log(leadingSpace.trimLeft()); // 输出: "Leading" (别名)
(3) trimEnd()
/ trimRight()
(ES2019)
trimEnd()
(或其别名 trimRight()
) 方法从字符串的末尾删除空白。
let trailingSpace = "Trailing ";
console.log(trailingSpace.trimEnd()); // 输出: "Trailing"
console.log(trailingSpace.trimRight()); // 输出: "Trailing" (别名)
2.3.5 替换与分割
(1) replace(searchValue, newValue)
replace()
方法返回一个由替换值 (newValue
) 替换部分或所有的模式 (searchValue
) 匹配项后的新字符串。
searchValue
: 可以是一个字符串或一个正则表达式。- 如果
searchValue
是字符串,则只有第一个匹配项会被替换。 - 如果
searchValue
是正则表达式,并且设置了全局标志 (g
),则所有匹配项都会被替换。
- 如果
newValue
: 可以是一个字符串或一个函数(用于更复杂的替换逻辑)。
let text = "The rain in Spain stays mainly in the plain.";
console.log(text.replace("rain", "sun")); // 只替换第一个: "The sun in Spain stays mainly in the plain."
let report = "Error: E001, Error: E002, Error: E003";
// 使用正则表达式和全局标志 'g' 替换所有 "Error:"
console.log(report.replace(/Error:/g, "Warning:"));
// 输出: "Warning: E001, Warning: E002, Warning: E003"
// 使用函数作为 newValue
let prices = "Items: $10, $20, $30";
// 将所有价格翻倍
let newPrices = prices.replace(/\$(\d+)/g, (match, p1) => {
// match 是整个匹配到的字符串, e.g., "$10"
// p1 是第一个捕获组, e.g., "10"
return "$" + (parseInt(p1) * 2);
});
console.log(newPrices); // 输出: "Items: $20, $40, $60"
(2) replaceAll(searchValue, newValue)
(ES2021)
replaceAll()
方法返回一个新字符串,其中所有 searchValue
的匹配项都被 newValue
替换。
searchValue
: 可以是一个字符串或一个没有设置全局标志 (g
) 的正则表达式。如果searchValue
是一个设置了全局标志的正则表达式,replaceAll
会抛出TypeError
,因为其设计初衷就是为了简化全局替换字符串的场景,避免用户忘记加g
标志。
let text = "apple banana apple orange apple";
// 替换所有 "apple"
console.log(text.replaceAll("apple", "grape"));
// 输出: "grape banana grape orange grape"
// 使用正则表达式 (不能带 g 标志)
console.log(text.replaceAll(/apple/ , "kiwi")); // 如果是 /apple/g 会报错
// 输出: "kiwi banana kiwi orange kiwi" (这里 /apple/ 行为上像带了 g)
// 与 replace 对比
console.log(text.replace("apple", "grape")); // 只替换第一个: "grape banana apple orange apple"
console.log(text.replace(/apple/g, "grape")); // 使用 replace + regex g 达到同样效果
replaceAll()
的引入使得全局替换字符串更加直接。
(3) split(separator, limit)
split()
方法使用指定的分隔符字符串将一个 String
对象分割成字符串数组,以将字符串分隔为子字符串,以确定每个拆分的位置。
separator
: 指定表示每个拆分应发生的点的字符串或正则表达式。- 如果
separator
是空字符串 (""
),则字符串会在每个字符之间分割。 - 如果
separator
未提供或在字符串中未找到,则返回的数组包含一个由整个字符串组成的元素。
- 如果
limit
(可选): 一个整数,限定返回的分割片段数量。
let csvData = "John,Doe,30,New York";
let parts = csvData.split(",");
console.log(parts); // 输出: ["John", "Doe", "30", "New York"]
let sentence = "The quick brown fox";
let words = sentence.split(" ");
console.log(words); // 输出: ["The", "quick", "brown", "fox"]
console.log(words[1]); // 输出: "quick"
let chars = "hello".split("");
console.log(chars); // 输出: ["h", "e", "l", "l", "o"]
let data = "item1|item2|item3|item4";
let limitedParts = data.split("|", 2);
console.log(limitedParts); // 输出: ["item1", "item2"]
2.3.6 其他实用方法
(1) charAt(index)
charAt()
方法从一个字符串中返回指定的字符。
let str = "Example";
console.log(str.charAt(0)); // 输出: "E"
console.log(str.charAt(str.length - 1)); // 输出: "e"
console.log(str.charAt(10)); // 输出: "" (空字符串,如果索引超出范围)
// 与方括号访问对比:
console.log(str[10]); // 输出: undefined
(2) charCodeAt(index)
charCodeAt()
方法返回指定索引处字符的 UTF-16 码元值(一个 0 到 65535 之间的整数)。
let str = "ABC";
console.log(str.charCodeAt(0)); // 输出: 65 (A的Unicode值)
console.log(str.charCodeAt(1)); // 输出: 66 (B的Unicode值)
console.log(str.charCodeAt(10)); // 输出: NaN (如果索引超出范围)
(3) padStart(targetLength, padString)
(ES2017)
padStart()
方法用另一个字符串 (padString
) 填充当前字符串的头部(多次执行,如果需要的话),直到结果字符串达到给定的长度 (targetLength
)。
let numStr = "5";
console.log(numStr.padStart(3, "0")); // 输出: "005"
console.log("abc".padStart(5, "*")); // 输出: "**abc"
console.log("12345".padStart(3, "0")); // 输出: "12345" (如果原字符串已达到或超过 targetLength)
console.log("abc".padStart(7, "123")); // 输出: "1231abc" (padString 会被重复和截断)
(4) padEnd(targetLength, padString)
(ES2017)
padEnd()
方法用另一个字符串 (padString
) 填充当前字符串的尾部(多次执行,如果需要的话),直到结果字符串达到给定的长度 (targetLength
)。
let label = "ID";
console.log(label.padEnd(5, "-")); // 输出: "ID---"
console.log("XYZ".padEnd(2, "0")); // 输出: "XYZ"
console.log("text".padEnd(10, "abc")); // 输出: "textabcabc"
(5) repeat(count)
(ES6)
repeat()
方法构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。
count
: 介于 0 和正无穷大之间的整数,表示在新构造的字符串中重复了多少遍原字符串。
console.log("*".repeat(5)); // 输出: "*****"
console.log("abc".repeat(3)); // 输出: "abcabcabc"
console.log("Hi".repeat(0)); // 输出: "" (空字符串)
// console.log("test".repeat(-1)); // RangeError: Invalid count value
表格:String 常用方法总结 (部分)
方法 | 描述 | 返回值类型 | 示例 (str = "JS World" ) |
---|---|---|---|
str.length | 获取字符串长度 | number | str.length (9) |
str.indexOf(val, start) | 查找子串首次出现索引,未找到返回-1 | number | str.indexOf("World") (3) |
str.lastIndexOf(val,end) | 查找子串最后一次出现索引,未找到返回-1 | number | str.lastIndexOf("o") (5) |
str.includes(val, pos) | 判断是否包含子串 | boolean | str.includes("JS") (true) |
str.startsWith(val, pos) | 判断是否以子串开头 | boolean | str.startsWith("JS") (true) |
str.endsWith(val, len) | 判断是否以子串结尾 | boolean | str.endsWith("rld") (true) |
str.slice(start, end) | 提取子串(支持负索引) | string | str.slice(3, 7) (“Worl”) |
str.substring(start, end) | 提取子串(负索引视为0,参数可交换) | string | str.substring(3, 0) ("JS ") |
str.toUpperCase() | 转换为大写 | string | str.toUpperCase() (“JS WORLD”) |
str.toLowerCase() | 转换为小写 | string | str.toLowerCase() (“js world”) |
str.trim() | 去除两端空白 | string | " Hi ".trim() (“Hi”) |
str.replace(sVal, nVal) | 替换(字符串只替首个,正则可全局) | string | str.replace("World", "Planet") (“JS Planet”) |
str.replaceAll(sV, nV) | 替换所有匹配项 (ES2021) | string | "a-a-a".replaceAll("a", "b") (“b-b-b”) |
str.split(sep, limit) | 将字符串分割成数组 | array | str.split(" ") ([“JS”, “World”]) |
str.charAt(index) | 返回指定索引的字符 | string | str.charAt(1) (“S”) |
str.padStart(len, padStr) | 头部填充字符串至指定长度 | string | "5".padStart(3, "0") (“005”) |
str.padEnd(len, padStr) | 尾部填充字符串至指定长度 | string | "ID".padEnd(4, ".") (“ID…”) |
str.repeat(count) | 重复字符串指定次数 | string | "go".repeat(2) (“gogo”) |
三、数字与字符串的交互
在实际编程中,我们经常需要在数字和字符串之间进行转换。
3.1 数字转换为字符串
有多种方法可以将数字转换为字符串:
3.1.1 String()
函数
String()
是一个全局函数,可以将任何类型的值转换为字符串。
let num = 123;
let numStr = String(num);
console.log(numStr); // 输出: "123"
console.log(typeof numStr); // 输出: "string"
console.log(String(3.14)); // 输出: "3.14"
console.log(String(NaN)); // 输出: "NaN"
console.log(String(Infinity)); // 输出: "Infinity"
3.1.2 num.toString(radix)
方法
正如前面 Number
方法部分介绍的,toString()
方法是数字对象的一个方法,用于将其转换为字符串。可以指定基数。
let amount = 255;
console.log(amount.toString()); // 输出: "255" (默认基数 10)
console.log(amount.toString(16)); // 输出: "ff" (十六进制)
console.log(amount.toString(2)); // 输出: "11111111" (二进制)
3.1.3 隐式转换 (如与字符串拼接时)
当数字与字符串使用 +
运算符进行操作时,数字会自动(隐式)转换为字符串,然后执行字符串拼接。
let score = 100;
let message = "Your score is: " + score;
console.log(message); // 输出: "Your score is: 100"
console.log(typeof message); // 输出: "string"
let result = "" + 42; // 将数字快速转换成字符串的技巧
console.log(result); // 输出: "42"
console.log(typeof result); // 输出: "string"
3.2 字符串转换为数字
同样,也有多种方法可以将字符串转换为数字:
3.2.1 Number()
函数
Number()
是一个全局函数,尝试将参数转换为数字。
- 对于纯数字字符串(包括浮点数、科学计数法),转换成功。
- 对于空字符串或只包含空白的字符串,转换为
0
。 - 对于包含非数字字符的字符串(除非是有效的数字表示,如
"0xFF"
),转换为NaN
。 "Infinity"
和"-Infinity"
会被正确转换为Infinity
和-Infinity
。true
转为1
,false
转为0
,null
转为0
。
console.log(Number("123")); // 输出: 123
console.log(Number("3.14")); // 输出: 3.14
console.log(Number(" 100 ")); // 输出: 100 (会去除前后空白)
console.log(Number("100px")); // 输出: NaN (包含非数字字符)
console.log(Number("")); // 输出: 0
console.log(Number("0xFF")); // 输出: 255 (能识别十六进制)
console.log(Number("Infinity")); // 输出: Infinity
3.2.2 parseInt(string, radix)
方法
前面已详细介绍,parseInt()
用于将字符串解析为整数。它会从字符串开头解析,直到遇到非数字字符(根据指定基数)或字符串末尾。
console.log(parseInt("42answer", 10)); // 输出: 42
console.log(parseInt(" -10.5", 10)); // 输出: -10 (取整)
3.2.3 parseFloat(string)
方法
前面已详细介绍,parseFloat()
用于将字符串解析为浮点数。
console.log(parseFloat("3.14 is pi")); // 输出: 3.14
console.log(parseFloat("Value: 2.5e3")); // 输出: NaN (开头不是数字)
3.2.4 隐式转换 (如使用一元 +
运算符)
在字符串前使用一元加号 +
运算符,是一种将字符串快速转换为数字的简洁方法,其行为与 Number()
函数类似。
let strNum = "2024";
let num = +strNum;
console.log(num); // 输出: 2024
console.log(typeof num); // 输出: "number"
console.log(+"-5.5"); // 输出: -5.5
console.log(+""); // 输出: 0
console.log(+"hello"); // 输出: NaN
其他算术运算符(-
, *
, /
, %
)也会尝试将操作数转换为数字,但这通常不推荐作为显式转换的手段,因为它可能降低代码可读性。
console.log("10" - "2"); // 输出: 8 (隐式转换为数字)
console.log("5" * "3"); // 输出: 15
3.3 常见问题与陷阱
3.3.1 浮点数运算不精确
已在 1.1.3 节中讨论。例如,0.1 + 0.2 !== 0.3
。处理金融等高精度场景时需特别小心,可使用 toFixed()
进行展示或库进行精确计算。
3.3.2 parseInt()
的进制问题
始终为 parseInt()
提供第二个参数 radix
,以避免因字符串开头的 “0” (在旧环境中可能被视为八进制) 或 “0x” 导致的解析混乱。
console.log(parseInt("010")); // 可能: 8 (旧行为) 或 10 (ES5+ 默认)
console.log(parseInt("010", 10)); // 明确: 10
console.log(parseInt("010", 8)); // 明确: 8
3.3.3 字符串拼接与数学运算的混淆
当 +
运算符的操作数中有一个是字符串时,会执行字符串拼接。如果意图是数学加法,确保所有操作数都是数字。
let val1 = "5";
let val2 = 10;
console.log(val1 + val2); // 输出: "510" (字符串拼接)
console.log(Number(val1) + val2); // 输出: 15 (数学加法)
console.log(+val1 + val2); // 输出: 15 (数学加法)
场景驱动: 从 HTML input 元素获取的值通常是字符串类型。如果需要进行数学运算,必须先将其转换为数字。
<input type="number" id="numA" value="10">
<input type="number" id="numB" value="20">
<button onclick="calculateSum()">Calculate</button>
<p id="result"></p>
<script>
function calculateSum() {
const numAStr = document.getElementById('numA').value; // "10" (string)
const numBStr = document.getElementById('numB').value; // "20" (string)
// 错误的方式:
// const sum = numAStr + numBStr; // "1020"
// 正确的方式:
const sum = Number(numAStr) + Number(numBStr); // 10 + 20 = 30
// 或者: const sum = +numAStr + +numBStr;
document.getElementById('result').textContent = 'Sum: ' + sum;
}
</script>
四、实际应用场景示例
4.1 数字应用场景
4.1.1 价格计算与格式化
电商网站、订单系统等经常需要计算商品总价并以特定格式(如保留两位小数)显示。
function calculateTotalPrice(items) {
let total = 0;
for (const item of items) {
total += item.price * item.quantity;
}
// 假设总价是 total,需要格式化为美元,保留两位小数
return `$${total.toFixed(2)}`;
}
let cartItems = [
{ name: "Laptop", price: 1200.50, quantity: 1 },
{ name: "Mouse", price: 25.99, quantity: 2 }
];
let formattedTotal = calculateTotalPrice(cartItems);
console.log(`Total cart value: ${formattedTotal}`); // 例如: Total cart value: $1252.48
4.1.2 数据校验
在处理用户输入或API数据时,验证数据是否为有效数字、是否在合理范围内等。
function processAgeInput(ageStr) {
const age = parseInt(ageStr, 10);
if (Number.isNaN(age)) {
return "Error: Age must be a number.";
}
if (!Number.isInteger(age) || age < 0 || age > 120) {
return "Error: Please enter a valid age (0-120).";
}
return `Age processed: ${age}`;
}
console.log(processAgeInput("25")); // Output: Age processed: 25
console.log(processAgeInput("abc")); // Output: Error: Age must be a number.
console.log(processAgeInput("30.5")); // Output: Error: Please enter a valid age (0-120). (parseInt会取整)
console.log(processAgeInput("150")); // Output: Error: Please enter a valid age (0-120).
4.2 字符串应用场景
4.2.1 用户输入处理
规范化用户输入,例如去除多余空格,统一大小写,以便后续比较或存储。
function normalizeUsername(username) {
if (typeof username !== 'string') {
return null;
}
// 去除前后空格,并转换为小写
return username.trim().toLowerCase();
}
let rawUsername = " JohnDoe123 ";
let normalized = normalizeUsername(rawUsername);
console.log(`Normalized username: '${normalized}'`); // Output: Normalized username: 'johndoe123'
if (normalized === "johndoe123") {
console.log("Username matches!");
}
4.2.2 构建动态消息
使用模板字符串生成包含动态数据的用户提示、日志信息等。
function createUserWelcomeMessage(user) {
const now = new Date();
const hours = now.getHours();
let timeOfDayGreeting;
if (hours < 12) {
timeOfDayGreeting = "Good morning";
} else if (hours < 18) {
timeOfDayGreeting = "Good afternoon";
} else {
timeOfDayGreeting = "Good evening";
}
return `${timeOfDayGreeting}, ${user.name}! Welcome back. You have ${user.unreadMessages} unread messages.`;
}
let currentUser = { name: "Alice", unreadMessages: 5 };
let welcomeMsg = createUserWelcomeMessage(currentUser);
console.log(welcomeMsg);
// 可能的输出: "Good afternoon, Alice! Welcome back. You have 5 unread messages."
4.2.3 URL 参数解析与构建
从 URL 中提取查询参数,或动态构建带参数的 URL。
// 简单 URL 参数解析示例
function getQueryParam(url, paramName) {
const queryString = url.split('?')[1];
if (!queryString) {
return null;
}
const params = queryString.split('&');
for (const param of params) {
const [key, value] = param.split('=');
if (key === paramName) {
return decodeURIComponent(value || ''); // 解码并处理没有值的参数
}
}
return null;
}
let sampleUrl = "[https://example.com/search?query=javascript&page=2](https://example.com/search?query=javascript&page=2)";
console.log(getQueryParam(sampleUrl, "query")); // Output: "javascript"
console.log(getQueryParam(sampleUrl, "page")); // Output: "2"
console.log(getQueryParam(sampleUrl, "sort")); // Output: null
// 构建 URL
function buildProductUrl(productId, category) {
const baseUrl = "[https://example.com/product](https://example.com/product)";
// 使用模板字符串和 encodeURIComponent 确保参数安全
return `${baseUrl}?id=${encodeURIComponent(productId)}&category=${encodeURIComponent(category)}`;
}
console.log(buildProductUrl("X123", "electronics/laptops"));
// Output: [https://example.com/product?id=X123&category=electronics%2Flaptops](https://example.com/product?id=X123&category=electronics%2Flaptops)
五、总结
今天我们深入探讨了 JavaScript 中两个基础且至关重要的数据类型:Number
和 String
。它们是我们日常编程中处理数据和信息的左膀右臂。
-
对于数字 (Number):
- JavaScript 中的数字不区分整数和浮点数,统一为
Number
类型。 - 我们了解了特殊的数值
Infinity
(无穷大) 和NaN
(非数字),以及如何使用Number.isFinite()
和Number.isNaN()
进行判断。 - 掌握了常用的
Number
方法,如toFixed()
(格式化小数位数)、parseInt()
(字符串转整数)、parseFloat()
(字符串转浮点数) 和toString()
(数字转字符串)。 - 需要注意浮点数运算的精度问题,在特定场景下采取相应措施。
- JavaScript 中的数字不区分整数和浮点数,统一为
-
对于字符串 (String):
- 字符串可以通过单引号、双引号或反引号(模板字符串)创建,其中模板字符串支持多行和插值,非常实用。
- 字符串是不可变的,所有修改字符串的方法都会返回新的字符串。
length
属性用于获取字符串长度,方括号可访问单个字符。- 我们学习了大量的字符串方法,包括:
- 查找定位:
indexOf()
,lastIndexOf()
,includes()
,startsWith()
,endsWith()
- 提取子串:
slice()
,substring()
(注意与slice
的区别) - 大小写转换:
toUpperCase()
,toLowerCase()
- 去除空白:
trim()
,trimStart()
,trimEnd()
- 替换与分割:
replace()
,replaceAll()
,split()
- 其他实用方法:
charAt()
,charCodeAt()
,padStart()
,padEnd()
,repeat()
- 查找定位:
-
数字与字符串的交互:
- 我们掌握了数字和字符串之间相互转换的多种方法,如
String()
,Number()
,toString()
,parseInt()
,parseFloat()
以及一元+
运算符等。 - 理解了隐式转换的场景和潜在的坑点,如
+
运算符在字符串和数字间的行为差异。
- 我们掌握了数字和字符串之间相互转换的多种方法,如
熟练掌握 Number
和 String
的特性和常用方法,将极大地提升您编写 JavaScript 代码的效率和质量。建议您亲自动手,多实践这些示例代码,加深理解。在下一篇文章中,我们将继续探索 JavaScript 的其他原始数据类型:布尔值 (Boolean
)、null
和 undefined
。敬请期待!