属性描述符可作用于原型对象以控制继承行为:在构造函数prototype或object.create创建的原型上定义,能统一约束所有实例;设writable/configurable为false可防修改删除,enumerable为false可隐藏方法;描述符仅影响目标对象本身,不穿透原型链;访问器属性(get/set)可实现共享的响应式逻辑。

属性描述符本身不直接“配合原型使用”,但它的作用对象可以是原型上的属性,从而精细控制继承行为和共享方法的访问规则。关键在于:你调用 Object.defineProperty 的目标对象,可以是构造函数的 prototype,也可以是通过 Object.create 创建的原型对象。
把描述符加在 prototype 上,统一约束所有实例
这是最常见也最有价值的用法。你想让所有实例共用某个方法,又希望它不可被意外覆盖或枚举,就定义在原型上,并用描述符锁定特性。
-
禁止修改和删除:设置
configurable: false和writable: false,确保方法不会被重写或删掉 -
隐藏不参与遍历:设
enumerable: false,这样for...in或Object.keys()就不会暴露该方法,保持接口干净 -
示例:
function Person(name) {<br> this.name = name;<br>}<br>// 在原型上定义一个受控方法<br>Object.defineProperty(Person.prototype, 'sayHi', {<br> value: function() { console.log(`Hi, I'm ${this.name}`); },<br> writable: false,<br> enumerable: false,<br> configurable: false<br>});<br><br>const p1 = new Person('Alice');<br>const p2 = new Person('Bob');<br>p1.sayHi(); // 正常调用<br>p1.sayHi = () => {}; // 无效,writable 为 false<br>console.log(p1.sayHi === p2.sayHi); // true,共用同一函数
给 Object.create 创建的原型加描述符,实现无类继承控制
当你用 Object.create(proto) 构建对象时,proto 就是新对象的 __proto__。你可以先用描述符定义好这个原型对象的属性,再让它被继承。
- 适合封装纯数据协议、配置模板或工具对象,避免污染
Object.prototype - 例如定义一个只读的默认配置原型:
const defaults = {};<br>Object.defineProperty(defaults, 'timeout', {<br> value: 5000,<br> writable: false,<br> enumerable: true,<br> configurable: false<br>});<br><br>const config = Object.create(defaults);<br>console.log(config.timeout); // 5000<br>config.timeout = 1000; // 无效<br>
注意:描述符对实例自有属性和原型属性的影响不同
描述符只作用于你明确传入的**那个对象**,不会自动“穿透”到原型链上:
- 如果你对
p1(实例)调用Object.defineProperty(p1, 'name', {...}),只影响p1自身的name,不影响Person.prototype或其他实例 - 如果你对
Person.prototype设置描述符,所有从它创建的实例都会继承该属性的行为(因为它们的__proto__指向这里) - 赋值操作(如
p1.name = 'Tom')默认会在实例上新建自有属性,**不会触发原型上描述符的writable限制** —— 那个限制只管“改原型上的同名属性”,不是管“所有地方能不能赋值”
实战小技巧:用 get/set 描述符做原型级代理逻辑
在原型上定义访问器属性,能让所有实例共享一套响应式逻辑,比如日志、校验或计算派生值。
Object.defineProperty(Person.prototype, 'fullName', {<br> get() { return this.name + ' (Person)'; },<br> enumerable: true,<br> configurable: true<br>});<br><br>const p = new Person('Leo');<br>console.log(p.fullName); // "Leo (Person)"- 这种
get是动态执行的,所有实例共享同一份逻辑,节省内存,也便于集中维护











