Object.create()
使用指定的原型对象及其属性去创建一个新的对象。
Object.create(proto[, propertiesObject]);
基本用法
// 创建原型为 null 的空对象(纯净字典,没有 toString 等方法)
const dict = Object.create(null);
// 等同于普通的 {}
const obj = Object.create(Object.prototype);
实现原型继承
const animal = {
describe() {
return `This is a ${this.type} named ${this.name}`;
}
};
const dog = Object.create(animal);
dog.type = 'dog';
dog.name = 'Rex';
console.log(dog.describe()); // "This is a dog named Rex"
console.log(Object.getPrototypeOf(dog) === animal); // true
带属性描述符创建
const person = Object.create(Object.prototype, {
name: { value: 'Tom', writable: true, enumerable: true, configurable: true },
age: { value: 25, writable: false, enumerable: true, configurable: false }
});
person.name = 'Jerry'; // 成功
person.age = 30; // 静默失败(严格模式报错)
Object.defineProperty()
直接在一个对象上定义新属性或修改现有属性,并返回这个对象。
Object.defineProperty(obj, prop, descriptor);
属性描述符
两种形式,不能混用:
数据描述符 — 有 value 和 writable。
存取描述符 — 有 get 和 set。
共有键值:
configurable— 是否可删除/重新配置。默认 false。enumerable— 是否可枚举。默认 false。
const obj = {};
// 数据描述符
Object.defineProperty(obj, 'name', {
value: 'hello',
writable: true,
enumerable: true,
configurable: true
});
// 存取描述符
let _age = 18;
Object.defineProperty(obj, 'age', {
get() { return _age; },
set(val) {
if (val < 0 || val > 150) throw new RangeError('Invalid age');
_age = val;
},
enumerable: true,
configurable: true
});
批量定义:Object.defineProperties
Object.defineProperties(obj, {
name: { value: 'Tom', writable: true, enumerable: true },
_secret: { value: 'hidden', enumerable: false }
});
console.log(Object.keys(obj)); // ['name'],_secret 不可枚举
实际应用:数据劫持
Vue 2.x 的响应式原理基于此:
function observe(obj) {
Object.keys(obj).forEach(key => {
let value = obj[key];
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}: ${value}`);
return value;
},
set(newVal) {
console.log(`设置 ${key}: ${value} -> ${newVal}`);
value = newVal;
}
});
});
}
const data = { message: 'Hello' };
observe(data);
data.message = 'World'; // 设置 message: Hello -> World
getter 和 setter
对象字面量中可以直接定义 getter/setter:
const circle = {
_radius: 5,
get radius() { return this._radius; },
set radius(value) {
if (value <= 0) throw new Error('Radius must be positive');
this._radius = value;
},
get area() {
return Math.PI * this._radius * this._radius;
}
};
console.log(circle.area); // 78.54...
circle.radius = 10;
console.log(circle.area); // 314.15...
Object.keys / values / entries
只返回对象自身的可枚举属性(不含继承属性)。
const person = { name: 'Tom', age: 25, job: 'developer' };
Object.keys(person); // ['name', 'age', 'job']
Object.values(person); // ['Tom', 25, 'developer']
Object.entries(person); // [['name','Tom'], ['age',25], ['job','developer']]
与 for…in 的区别
const parent = { inherited: true };
const child = Object.create(parent);
child.own = 'yes';
for (const key in child) {
console.log(key); // 'own', 'inherited' — 包含继承属性
}
Object.keys(child); // ['own'] — 只有自身属性
对象的冻结和密封
ES5 提供三个层级的对象保护。
Object.preventExtensions — 禁止扩展
不能添加新属性,但可以修改和删除已有属性。
const obj = { a: 1, b: 2 };
Object.preventExtensions(obj);
obj.c = 3; // 静默失败
obj.a = 10; // 可以修改
delete obj.b; // 可以删除
Object.seal — 密封
不能添加、不能删除、不能修改描述符,但可以修改属性值。
const obj = { x: 1 };
Object.seal(obj);
obj.x = 100; // 可以修改值
obj.z = 3; // 不能添加
delete obj.x; // 不能删除
Object.freeze — 冻结
最严格。不能添加、删除、修改任何属性。
const obj = { name: 'frozen', list: [1, 2, 3] };
Object.freeze(obj);
obj.name = 'changed'; // 静默失败
注意:Object.freeze 是浅冻结,嵌套对象不受影响:
obj.list.push(4); // 可以!嵌套对象未被冻结
// 深冻结实现
function deepFreeze(obj) {
Object.getOwnPropertyNames(obj).forEach(name => {
const value = obj[name];
if (typeof value === 'object' && value !== null) {
deepFreeze(value);
}
});
return Object.freeze(obj);
}
三种保护对比
| 操作 | preventExtensions | seal | freeze |
|---|---|---|---|
| 添加属性 | 禁止 | 禁止 | 禁止 |
| 删除属性 | 允许 | 禁止 | 禁止 |
| 修改属性值 | 允许 | 允许 | 禁止 |
| 修改描述符 | 允许 | 禁止 | 禁止 |
实际应用
// 不可变配置对象
const CONFIG = Object.freeze({
API_BASE: 'https://api.example.com',
TIMEOUT: 5000
});
// 纯净字典(无原型链污染)
const safeMap = Object.create(null);
console.log('toString' in safeMap); // false
// 用 defineProperty 实现常量
Object.defineProperty(app, 'VERSION', {
value: '1.0.0',
writable: false,
configurable: false
});