字符串与复杂类型
String 类型
String 类型表示零或多个 16 位 Unicode 字符序列。字符串是不可变的(immutable)。
基本特性
javascript
// 字符串字面量
let firstName = "John"
let lastName = "Doe"
let fullName = `John Doe` // ES6 模板字符串
// 字符串不可变性
let lang = "Java"
lang = lang + "Script"
// 实际过程:
// 1. 创建包含 "Java" 的字符串
// 2. 创建包含 "Script" 的字符串
// 3. 创建包含 "JavaScript" 的新字符串
// 4. 销毁原始字符串转义序列
| 转义序列 | 含义 |
|---|---|
\n | 换行 |
\t | 制表符 |
\b | 退格 |
\r | 回车 |
\f | 换页 |
\\ | 反斜杠 |
\' | 单引号 |
\" | 双引号 |
\xnn | 十六进制字符码 |
\unnnn | Unicode 字符 |
javascript
let text = "First line\nSecond line"
let path = "C:\\Users\\John\\Desktop"
let quote = 'He said, "Hello"'字符串方法
字符方法
javascript
let str = "Hello World"
// 访问字符
str.charAt(0) // "H"
str[0] // "H"(ES5)
str.charCodeAt(0) // 72(ASCII 码)
str.codePointAt(0) // 72(ES6,支持 Unicode)
// fromCharCode / fromCodePoint
String.fromCharCode(72, 101, 108, 108, 111) // "Hello"
String.fromCodePoint(0x1f600) // "😀"字符串操作方法
javascript
let str = "Hello World"
// 拼接
str.concat("!", "!") // "Hello World!!"
str + "!" + "!" // 更常用
// 截取
str.slice(0, 5) // "Hello"
str.substring(0, 5) // "Hello"
str.substr(0, 5) // "Hello"(不推荐使用)
// 位置方法
// ... 中间省略 ...
// 重复
str.repeat(3) // "Hello WorldHello WorldHello World"(ES6)
// 填充
str.padStart(15, "*") // "****Hello World"(ES2017)
str.padEnd(15, "*") // "Hello World****"(ES2017)字符串分割和替换
javascript
let str = "Hello,World,JavaScript"
// 分割
str.split(",") // ["Hello", "World", "JavaScript"]
str.split(",", 2) // ["Hello", "World"]
str.split("") // ["H", "e", "l", "l", "o", ...]
// 替换
let text = "cat sat on the mat"
text.replace("at", "ond") // "cond sat on the mat"(只替换第一个)
text.replace(/at/g, "ond") // "cond sond on the mond"(全局替换)
// replaceAll(ES2021)
text.replaceAll("at", "ond") // "cond sond on the mond"模式匹配
javascript
let str = "Hello World"
// match
str.match(/o/g) // ["o", "o"]
str.match("o") // ["o", index: 4, input: "Hello World", groups: undefined]
// search
str.search(/o/) // 4
// matchAll(ES2020)
const matches = str.matchAll(/o/g)
for (const match of matches) {
console.log(match)
}转为字符串
toString() 方法
javascript
// 基本用法
let age = 11
age.toString() // "11"
let found = true
found.toString() // "true"
// 数值的 toString() 可以指定进制
let num = 10
num.toString() // "10"
num.toString(2) // "1010"(二进制)
num.toString(8) // "12"(八进制)
num.toString(16) // "a"(十六进制)
// null 和 undefined 没有 toString() 方法
// null.toString() // TypeError
// undefined.toString() // TypeErrorString() 函数
javascript
// String() 可以处理 null 和 undefined
String(10) // "10"
String(true) // "true"
String(null) // "null"
String(undefined) // "undefined"
// 等价于
let value1 = 10
let value2 = true
let value3 = null
let value4
String(value1) // "10"
String(value2) // "true"
String(value3) // "null"
String(value4) // "undefined"模板字面量(ES6)
模板字面量使用反引号(`)定义,支持多行字符串和字符串插值。
基本用法
javascript
// 多行字符串
let myMultiLineString = "first line\nsecond line"
let myMultiLineTemplateLiteral = `first line
second line`
console.log(myMultiLineString === myMultiLineTemplateLiteral) // true
// HTML 模板
let pageHTML = `
<div>
<a href="#">
<span>Jake</span>
</a>
</div>
`⚠️ 注意空格:模板字面量会保留反引号内的所有空格和换行。 ```javascript // 这个模板字面量在换行符之后有 25 个空格符 let myTemplateLiteral =
first line second lineconsole.log(myTemplateLiteral.length) // 47 // 这个模板字面量以一个换行符开头 let secondTemplateLiteral =first line second lineconsole.log(secondTemplateLiteral[0] === "\n") // truecode
字符串插值
javascript
let value = 5
let exponent = "second"
// 旧方式
let interpolatedString = value + " to the " + exponent + " power is " + value * value
// 新方式(模板字面量)
let interpolatedTemplateLiteral = `${value} to the ${exponent} power is ${value * value}`
console.log(interpolatedString) // "5 to the second power is 25"
console.log(interpolatedTemplateLiteral) // "5 to the second power is 25"
// 任何 JavaScript 表达式都可以用于插值
console.log(`Hello, ${`World`}!`) // "Hello, World!"
// 函数调用
function capitalize(word) {
return `${word[0].toUpperCase()}${word.slice(1)}`
}
console.log(`${capitalize("hello")}, ${capitalize("world")}!`) // "Hello, World!"
// 对象的 toString()
let foo = { toString: () => "World" }
console.log(`Hello, ${foo}!`) // "Hello, World!"标签函数
标签函数可以自定义模板字面量的插值行为。
javascript
let a = 6
let b = 9
// 标签函数接收参数:
// - strings: 被插值分隔的字符串数组
// - ...expressions: 每个插值表达式的值
function simpleTag(strings, ...expressions) {
console.log(strings) // ["", " + ", " = ", ""]
expressions.forEach((expr) => console.log(expr)) // 6, 9, 15
return "foobar"
}
let taggedResult = simpleTag`${a} + ${b} = ${a + b}`
console.log(taggedResult) // "foobar"
// 实现默认行为
function zipTag(strings, ...expressions) {
return strings[0] + expressions.map((e, i) => `${e}${strings[i + 1]}`).join("")
}
let untaggedResult = `${a} + ${b} = ${a + b}`
let taggedResult = zipTag`${a} + ${b} = ${a + b}`
console.log(untaggedResult) // "6 + 9 = 15"
console.log(taggedResult) // "6 + 9 = 15"原始字符串
String.raw 标签函数可以获取原始的模板字面量内容。
javascript
// 转义序列不会被转换
console.log(`\u00A9`) // ©
console.log(String.raw`\u00A9`) // \u00A9
console.log(`first line\nsecond line`)
// first line
// second line
console.log(String.raw`first line\nsecond line`) // "first line\nsecond line"
// 注意:实际的换行符不会被转换
console.log(String.raw`first line
second line`)
// first line
// second line字符串迭代器(ES6)
javascript
let str = "😀hello"
// for...of 迭代(正确处理 Unicode)
for (let char of str) {
console.log(char) // 😀, h, e, l, l, o
}
// 转换为数组
let chars = [...str] // ["😀", "h", "e", "l", "l", "o"]
let charsArray = Array.from(str) // ["😀", "h", "e", "l", "l", "o"]Symbol 类型
Symbol(符号)是 ES6 新增的原始类型,表示唯一的、不可变的标识符。
基本用法
javascript
// 创建符号
let sym1 = Symbol()
let sym2 = Symbol("description") // 可选的描述字符串
let sym3 = Symbol("description")
console.log(sym2 === sym3) // false(每个 Symbol 都是唯一的)
// typeof 检测
console.log(typeof sym1) // "symbol"Symbol 的特点
javascript
// 1. 每个符号都是唯一的
const sym1 = Symbol("id")
const sym2 = Symbol("id")
console.log(sym1 === sym2) // false
// 2. 符号不能使用 new 创建
// const sym = new Symbol() // TypeError
// 3. 符号可以转换为字符串或布尔值
console.log(String(sym1)) // "Symbol(id)"
console.log(sym1.toString()) // "Symbol(id)"
console.log(Boolean(sym1)) // true
// 4. 符号不能转换为数值
// console.log(Number(sym1)) // TypeError作为对象属性名
javascript
const sym = Symbol("id")
const user = {
name: "John",
[sym]: 12345 // 使用符号作为属性名
}
console.log(user[sym]) // 12345
console.log(user.sym) // undefined(不能使用点号访问)
// 符号属性不会被常规方法枚举
for (let key in user) {
console.log(key) // 只输出 "name"
}
console.log(Object.keys(user)) // ["name"]
console.log(Object.getOwnPropertyNames(user)) // ["name"]
// 获取符号属性
console.log(Object.getOwnPropertySymbols(user)) // [Symbol(id)]
console.log(Reflect.ownKeys(user)) // ["name", Symbol(id)]全局符号注册表
javascript
// 创建全局符号
let globalSym1 = Symbol.for("foo")
let globalSym2 = Symbol.for("foo")
console.log(globalSym1 === globalSym2) // true
// 获取全局符号的键
console.log(Symbol.keyFor(globalSym1)) // "foo"
// 普通符号不在全局注册表中
let localSym = Symbol("foo")
console.log(Symbol.keyFor(localSym)) // undefined内置符号(Well-known Symbols)
ES6 定义了一组内置符号,用于暴露 JavaScript 内部行为。
javascript
// Symbol.iterator - 定义对象的默认迭代器
const myIterable = {
[Symbol.iterator]() {
let step = 0
return {
next() {
step++
if (step <= 3) {
return { value: step, done: false }
}
return { value: undefined, done: true }
}
// ... 中间省略 ...
static [Symbol.hasInstance](instance) {
return Array.isArray(instance)
}
}
console.log([] instanceof MyArray) // trueSymbol 使用场景
1. 私有属性
javascript
// 在 ES2022 之前,Symbol 常用于模拟私有属性
const _privateField = Symbol("private")
class MyClass {
constructor() {
this[_privateField] = "private value"
}
getPrivateField() {
return this[_privateField]
}
}
const obj = new MyClass()
console.log(obj[_privateField]) // "private value"(仍可访问)
console.log(Object.keys(obj)) // [](不可枚举)2. 避免属性冲突
javascript
// 当扩展第三方对象时,避免覆盖已有属性
const libraryObject = { name: "Library" }
const myId = Symbol("myId")
libraryObject[myId] = "my unique identifier"
console.log(libraryObject[myId]) // "my unique identifier"3. 元数据
javascript
// 为对象添加元数据而不影响对象结构
const metadata = Symbol("metadata")
function addMetadata(obj, data) {
obj[metadata] = data
}
function getMetadata(obj) {
return obj[metadata]
}
const user = { name: "John" }
addMetadata(user, { createdAt: Date.now(), version: 1 })
console.log(getMetadata(user)) // { createdAt: ..., version: 1 }BigInt 类型
BigInt 是 ES2020 新增的数据类型,用于表示任意精度的整数。
创建 BigInt
javascript
// 方法 1:在数字后面加 n
let bigInt1 = 9007199254740991n
let bigInt2 = 123456789012345678901234567890n
// 方法 2:使用 BigInt() 函数
let bigInt3 = BigInt(9007199254740991)
let bigInt4 = BigInt("123456789012345678901234567890")
let bigInt5 = BigInt("0x1fffffffffffff") // 十六进制BigInt 的特点
1. 不能与 Number 混合运算
javascript
let bigInt = 100n
let number = 100
// console.log(bigInt + number) // TypeError: Cannot mix BigInt and other types
// 需要先转换
console.log(bigInt + BigInt(number)) // 200n
console.log(Number(bigInt) + number) // 200
// 注意:Number(bigInt) 可能丢失精度
let hugeBigInt = 9007199254740993n
console.log(Number(hugeBigInt)) // 9007199254740992(精度丢失!)2. 比较操作
javascript
let bigInt = 100n
let number = 100
console.log(bigInt == number) // true(宽松相等,会进行类型转换)
console.log(bigInt === number) // false(严格相等,类型不同)
console.log(bigInt > 50) // true
console.log(bigInt > 50n) // true
// 与其他类型比较
console.log(100n > 99) // true
console.log(100n < 101) // true3. 数学运算
javascript
let a = 10n
let b = 20n
console.log(a + b) // 30n
console.log(a - b) // -10n
console.log(a * b) // 200n
console.log(a / b) // 0n(注意:BigInt 除法会向下取整)
console.log(a % b) // 10n
console.log(7n / 2n) // 3n(向下取整)
console.log(-7n / 2n) // -4n(向下取整)
// 幂运算(ES2020)
console.log(2n ** 100n) // 1267650600228229401496703205376n
// 不支持的运算
// console.log(Math.sqrt(16n)) // TypeError
console.log(16n ** (1n / 2n)) // 4n(可以使用幂运算替代)4. 类型检测
javascript
let bigInt = 123n
console.log(typeof bigInt) // "bigint"
console.log(bigInt instanceof BigInt) // false(原始类型)
console.log(BigInt(123) === 123n) // trueBigInt 使用场景
javascript
// 1. 处理大整数(超过 Number.MAX_SAFE_INTEGER)
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
// Number 会丢失精度
console.log(9007199254740992 === 9007199254740993) // true(错误!)
// BigInt 可以精确表示
console.log(9007199254740992n === 9007199254740993n) // false(正确)
// 2. 加密计算
const largePrime = 2152302898747n
// 3. 高精度时间戳
const timestamp = 1625097600000000000n // 纳秒级时间戳
// 4. 处理大数据 ID
const bigId = BigInt("123456789012345678901234567890")BigInt 与 Number 对比
| 特性 | Number | BigInt |
|---|---|---|
| 精度 | 双精度浮点数,最多 15-17 位有效数字 | 任意精度整数 |
| 范围 | ±1.7976931348623157e+308 | 无限制(受内存限制) |
| 小数 | 支持小数 | 只支持整数 |
| 科学记数法 | 支持 | 不支持 |
| Math 方法 | 支持 | 不支持 |
| JSON 序列化 | 支持 | 不支持(需要转换) |
javascript
// BigInt 与 JSON
const bigIntValue = 123n
// JSON.stringify(bigIntValue) // TypeError
// 解决方案:转换为字符串
JSON.stringify({ value: bigIntValue.toString() }) // '{"value":"123"}'
// 或使用自定义序列化
function bigIntReplacer(key, value) {
return typeof value === "bigint" ? value.toString() : value
}
JSON.stringify({ id: 123n }, bigIntReplacer) // '{"id":"123"}'