JavaScript class 中无法定义真正只读字段,但可通过四种方式模拟:1. Object.defineProperty 定义 getter-only 属性;2. 私有字段+#+公共 getter(推荐);3. Object.freeze() 冻结配置对象;4. TypeScript 的 readonly + as const 编译期保障。

在 JavaScript class 中无法直接定义真正不可变的“只读配置项”(如 Java 的 final 或 TypeScript 的 readonly 字段),但可以通过几种方式**模拟只读语义**,让实例属性在初始化后无法被意外修改。关键在于:**只读 ≠ 不可访问,而是“不可赋值”或“赋值即报错”**。
1. 使用 Object.defineProperty 在构造函数中定义 getter-only 属性
这是最接近“运行时只读”的方式:不设 setter,且将属性设为不可写、不可配置,从而阻止后续修改。
class ApiClient {
constructor(config) {
// 定义只读的 baseUrl
Object.defineProperty(this, 'baseUrl', {
value: config.baseUrl || 'https://www.php.cn/link/710ba53b0d353329706ee1bedf4b9b39',
writable: false, // 禁止重新赋值
enumerable: true, // 可枚举(for...in / Object.keys 中可见)
configurable: false // 禁止 delete 或重新 define
});
<pre class="brush:php;toolbar:false;">// 同样定义只读的 timeout
Object.defineProperty(this, 'timeout', {
value: config.timeout ?? 5000,
writable: false,
enumerable: true,
configurable: false
});} }
✅ 效果:一旦创建实例,client.baseUrl = 'xxx' 不会生效(严格模式下抛 TypeError);delete client.baseUrl 失败。
2. 使用私有字段 + 公共 getter(ES2022+,推荐)
利用 # 私有字段 + public getter 组合,既隐藏原始值,又控制对外暴露方式:
立即学习“Java免费学习笔记(深入)”;
class ApiClient {
#baseUrl;
#timeout;
<p>constructor(config) {
this.#baseUrl = config.baseUrl || '<a href="https://www.php.cn/link/710ba53b0d353329706ee1bedf4b9b39">https://www.php.cn/link/710ba53b0d353329706ee1bedf4b9b39</a>';
this.#timeout = config.timeout ?? 5000;
}</p><p>get baseUrl() {
return this.#baseUrl;
}</p><p>get timeout() {
return this.#timeout;
}</p><p>// 不提供 setter → 外部无法修改
}
✅ 效果:外部只能读取 client.baseUrl,尝试赋值(client.baseUrl = 'x')静默失败(非严格模式)或报错(严格模式);私有字段无法从外部访问,天然防篡改。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
3. 配合 Object.freeze() 冻结整个配置对象(适用于嵌套只读结构)
如果配置项本身是对象(如 { headers: {}, retry: { max: 3 } }),可在构造时冻结它,防止深层修改:
class ApiClient {
#config;
<p>constructor(rawConfig) {
const config = {
baseUrl: rawConfig.baseUrl || '<a href="https://www.php.cn/link/710ba53b0d353329706ee1bedf4b9b39">https://www.php.cn/link/710ba53b0d353329706ee1bedf4b9b39</a>',
timeout: rawConfig.timeout ?? 5000,
headers: { ...rawConfig.headers },
retry: { ...rawConfig.retry }
};
this.#config = Object.freeze(config); // 冻结顶层属性
}</p><p>get config() {
return this.#config; // 返回冻结后的只读副本
}
}
⚠️ 注意:Object.freeze() 是浅冻结,若需深只读,需递归冻结或使用 structuredClone() + 冻结(现代环境)。
4. TypeScript 中更严格的只读保障(编译期)
如果你用 TypeScript,可结合 readonly 修饰符 + as const 提升字面量类型精度:
class ApiClient {
readonly baseUrl: string;
readonly timeout: number;
readonly headers: Readonly<Record<string, string>>;
<p>constructor(config: {
baseUrl?: string;
timeout?: number;
headers?: Record<string, string>;
}) {
this.baseUrl = config.baseUrl ?? '<a href="https://www.php.cn/link/710ba53b0d353329706ee1bedf4b9b39">https://www.php.cn/link/710ba53b0d353329706ee1bedf4b9b39</a>';
this.timeout = config.timeout ?? 5000;
this.headers = Object.freeze(config.headers ?? {}) as const;
}
}
✅ 编译期报错:任何对 this.baseUrl 的赋值都会被 TS 拦截;as const 让 headers 成为字面量只读类型。
不复杂但容易忽略:纯 JavaScript 没有语言级只读字段,所以必须靠 defineProperty、私有字段 + getter 或冻结对象来实现语义只读。优先推荐私有字段 + getter 方案——清晰、安全、无需 polyfill,且现代浏览器和 Node.js 均已广泛支持。

















