javascript中无法实现绝对只读属性,但可通过object.defineproperty设writable:false和configurable:false在原型上实现运行时不可修改、不可删除、不可重新配置的只读效果。

JavaScript 中无法真正实现绝对的只读属性(尤其在原型上),但可以通过 Object.defineProperty 配合 writable: false 和 configurable: false 在原型对象上设置**运行时不可修改、不可删除、不可重新配置**的属性,达到事实上的只读效果。
在原型上定义只读数据属性
直接在构造函数的 prototype 上使用 Object.defineProperty,禁用写入与配置能力:
-
必须指定
writable: false:防止赋值覆盖(如obj.prop = 42失效) -
强烈建议加上
configurable: false:否则后续仍可用defineProperty改回可写,或用delete删除该属性 - 不能省略
value(数据属性)或get(访问器属性),否则会报错
示例:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
function Person() {}
Object.defineProperty(Person.prototype, 'species', {
value: 'Homo sapiens',
writable: false,
enumerable: true,
configurable: false
});
const p = new Person();
console.log(p.species); // "Homo sapiens"
p.species = 'Alien'; // 静默失败(非严格模式)或抛 TypeError(严格模式)
delete p.species; // false(非严格)或 TypeError(严格)
用 getter 模拟只读计算属性
当属性值需要动态计算(如依赖实例状态),应使用 get 访问器而非 value。此时无需设 writable,只需确保没有 set,并设 configurable: false 防止被重定义:
- 只提供
get,不提供set→ 尝试赋值会静默忽略(非严格)或报错(严格) -
configurable: false是关键:否则别人可删掉 getter 再重新定义为可写属性 - 通常设
enumerable: true以保持属性可见性(如for...in)
示例:
Object.defineProperty(Person.prototype, 'isHuman', {
get() {
return this.species === 'Homo sapiens';
},
enumerable: true,
configurable: false
});
注意原型只读属性的局限性
这类保护仅作用于「通过原型链访问」的场景,存在几个常见绕过点:
-
实例自身属性优先级更高:若实例已拥有同名属性(
p.species = 'test'),则读写都走实例属性,完全绕过原型定义 -
无法阻止原型被替换:
p.__proto__ = {}或Object.setPrototypeOf(p, null)会切断访问路径 -
不防篡改构造函数本身:有人仍可改写
Person.prototype.species—— 除非你同时冻结整个原型:Object.freeze(Person.prototype)
若需更强防护,可在定义后立即冻结原型:Object.freeze(Person.prototype)(它会递归将所有自有属性设为 configurable: false 并禁止新增属性)。
冻结整个原型对象(推荐组合方案)
单个 defineProperty 管理成本高;对整个原型调用 Object.freeze 更简洁,且能一并防止新增/删除/重配置:
- 先用
defineProperty设置好只读属性(含configurable: false) - 再执行
Object.freeze(Person.prototype)—— 此时即使漏设configurable,freeze 也会补上 - 注意:
freeze不影响实例属性,也不递归冻结嵌套对象(需手动处理)
这样既保障了原型层属性的只读性,又提升了代码健壮性。










