【JavaScript-Day 7】全面解析 Number 与 String:JS 数据核心操作指南

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 陷阱:深入理解 letconst 的妙用
06-【JavaScript-Day 6】从零到精通:JavaScript 原始类型 String, Number, Boolean, Null, Undefined, Symbol, BigInt 详解
07-【JavaScript-Day 7】全面解析 Number 与 String:JS 数据核心操作指南


文章目录


前言

欢迎来到 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): 以 0b0B 开头,如 0b101 (十进制的 5)。
  • 八进制 (Octal): 在严格模式下,旧的以 0 开头的八进制表示法(如 012)会被视为十进制或语法错误。ES6 引入了以 0o0O 开头的表示法,如 0o12 (十进制的 10)。
  • 十六进制 (Hexadecimal): 以 0x0X 开头,如 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 的特性:

  1. 任何涉及 NaN 的算术运算(除了某些位运算)的结果都是 NaN
  2. 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 为基础来解析,除非数字以 0x0X 开头,那么基数是 16。ES5 之后,如果 radix 未指定且字符串不以 0x0X 开头,则默认为 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)判断是否为有限数值booleanNumber.isFinite(num) (true)
Number.isInteger(val)判断是否为整数booleanNumber.isInteger(num) (false)
Number.isNaN(val)判断是否为 NaN (严格)booleanNumber.isNaN(0/0) (true)
num.toFixed(digits)转换为字符串,保留指定小数位数 (四舍五入)stringnum.toFixed(2) (“123.46”)
num.toPrecision(prec)转换为字符串,保留指定有效数字位数 (四舍五入)stringnum.toPrecision(4) (“123.5”)
parseInt(str, radix)将字符串解析为整数 (建议指定 radix)numberparseInt("123px", 10) (123)
parseFloat(str)将字符串解析为浮点数numberparseFloat("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() 方法判断一个字符串是否包含指定的子字符串,返回 truefalse

  • 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() 方法用来判断当前字符串是否以另一个给定的子字符串开头,并根据判断结果返回 truefalse

  • 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() 方法用来判断当前字符串是否以另一个给定的子字符串结尾,并根据判断结果返回 truefalse

  • 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 大于 endIndexsubstring 会交换这两个参数。
  • 任何小于 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获取字符串长度numberstr.length (9)
str.indexOf(val, start)查找子串首次出现索引,未找到返回-1numberstr.indexOf("World") (3)
str.lastIndexOf(val,end)查找子串最后一次出现索引,未找到返回-1numberstr.lastIndexOf("o") (5)
str.includes(val, pos)判断是否包含子串booleanstr.includes("JS") (true)
str.startsWith(val, pos)判断是否以子串开头booleanstr.startsWith("JS") (true)
str.endsWith(val, len)判断是否以子串结尾booleanstr.endsWith("rld") (true)
str.slice(start, end)提取子串(支持负索引)stringstr.slice(3, 7) (“Worl”)
str.substring(start, end)提取子串(负索引视为0,参数可交换)stringstr.substring(3, 0) ("JS ")
str.toUpperCase()转换为大写stringstr.toUpperCase() (“JS WORLD”)
str.toLowerCase()转换为小写stringstr.toLowerCase() (“js world”)
str.trim()去除两端空白string" Hi ".trim() (“Hi”)
str.replace(sVal, nVal)替换(字符串只替首个,正则可全局)stringstr.replace("World", "Planet") (“JS Planet”)
str.replaceAll(sV, nV)替换所有匹配项 (ES2021)string"a-a-a".replaceAll("a", "b") (“b-b-b”)
str.split(sep, limit)将字符串分割成数组arraystr.split(" ") ([“JS”, “World”])
str.charAt(index)返回指定索引的字符stringstr.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 转为 1false 转为 0null 转为 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 中两个基础且至关重要的数据类型:NumberString。它们是我们日常编程中处理数据和信息的左膀右臂。

  • 对于数字 (Number):

    1. JavaScript 中的数字不区分整数和浮点数,统一为 Number 类型。
    2. 我们了解了特殊的数值 Infinity (无穷大) 和 NaN (非数字),以及如何使用 Number.isFinite()Number.isNaN() 进行判断。
    3. 掌握了常用的 Number 方法,如 toFixed() (格式化小数位数)、parseInt() (字符串转整数)、parseFloat() (字符串转浮点数) 和 toString() (数字转字符串)。
    4. 需要注意浮点数运算的精度问题,在特定场景下采取相应措施。
  • 对于字符串 (String):

    1. 字符串可以通过单引号、双引号或反引号(模板字符串)创建,其中模板字符串支持多行和插值,非常实用。
    2. 字符串是不可变的,所有修改字符串的方法都会返回新的字符串。
    3. length 属性用于获取字符串长度,方括号可访问单个字符。
    4. 我们学习了大量的字符串方法,包括:
      • 查找定位indexOf(), lastIndexOf(), includes(), startsWith(), endsWith()
      • 提取子串slice(), substring() (注意与 slice 的区别)
      • 大小写转换toUpperCase(), toLowerCase()
      • 去除空白trim(), trimStart(), trimEnd()
      • 替换与分割replace(), replaceAll(), split()
      • 其他实用方法charAt(), charCodeAt(), padStart(), padEnd(), repeat()
  • 数字与字符串的交互:

    1. 我们掌握了数字和字符串之间相互转换的多种方法,如 String(), Number(), toString(), parseInt(), parseFloat() 以及一元 + 运算符等。
    2. 理解了隐式转换的场景和潜在的坑点,如 + 运算符在字符串和数字间的行为差异。

熟练掌握 NumberString 的特性和常用方法,将极大地提升您编写 JavaScript 代码的效率和质量。建议您亲自动手,多实践这些示例代码,加深理解。在下一篇文章中,我们将继续探索 JavaScript 的其他原始数据类型:布尔值 (Boolean)、nullundefined。敬请期待!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴师兄大模型

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值