在 TypeScript 和 JavaScript 的世界里,const 和 readonly 经常被放在一起比较,因为它们都服务于同一个目标:防止变量或属性被修改。
简单来说:const 作用于变量(Variable),而 readonly 作用于属性(Property)。
1. 核心差异对照表
| 特性 | const | readonly |
|---|---|---|
| 所属语言 | JavaScript (ES6+) | TypeScript (特有) |
| 约束对象 | 变量引用(该变量不能重新指向新对象) | 类或接口的属性(该属性在初始化后不能修改) |
| 检查时机 | 运行时 (Runtime) | 编译时 (Compile-time) |
| 初始化 | 声明时必须赋值 | 可以在声明时或在构造函数(Constructor)中赋值 |
| 适用范围 | 局部变量、全局常量 | 类(Class)、接口(Interface)、类型别名(Type) |
2. 深入理解 const
const 是 JavaScript 的原生关键字。它保证的是变量名和内存地址之间的绑定关系不可变。
-
基本类型: 值确实不可变(如数字、字符串)。
-
引用类型(对象/数组): 内容是可变的,只是不能把变量重新指向另一个对象。
TypeScript
const user = { name: "Alice" };
user.name = "Bob"; // ✅ 允许!因为 user 依然指向同一个对象
// user = { name: "Charlie" }; // ❌ 报错:无法分配到常数
3. 深入理解 readonly
readonly 是 TypeScript 提供的类型系统修饰符。它更像是一个“只读协议”,告诉编译器:“这个属性一旦确定,别让任何人动它”。
-
灵活性: 它允许在构造函数中进行逻辑计算后再赋值,而
const必须在声明那一刻写死。 -
编译后消失: 编译成 JS 后,
readonly会消失,它不影响运行时的行为。
TypeScript
class Robot {
readonly serialNumber: string;
constructor(id: number) {
// 允许在构造函数内初始化
this.serialNumber = `SN-${id}-${Math.random()}`;
}
updateId() {
// this.serialNumber = "NEW-ID"; // ❌ 报错:只读属性不可修改
}
}
4. 组合拳:最严格的限制
如果你想要一个变量既不能重新指向,其内部属性也不能被修改,你需要结合两者使用:
TypeScript
interface Point {
readonly x: number;
readonly y: number;
}
const p1: Point = { x: 10, y: 20 };
// p1 = { x: 1, y: 2 }; // ❌ const 阻止了重新赋值
// p1.x = 5; // ❌ readonly 阻止了修改属性
5. 什么时候用哪个?
-
如果你在声明一个局部的临时变量,或者一个全局的配置常量,使用
const。 -
如果你在定义接口、类或对象字面量,且希望外部调用者不要随意修改里面的成员,使用
readonly。 -
如果你正在处理一个数组且不希望它被 push/pop/splice,使用
ReadonlyArray<T>。
你需要我演示一下如何利用 Readonly<T> 快速把一个现有的复杂接口转换成全只读模式吗?