Skip to content
imshengli blog
Go back

ES5 Object:defineProperty、getter/setter 与对象冻结

Object.create、defineProperty、getter/setter、对象冻结与密封,ES5 对象操作完整指南。

· 5 min

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。

共有键值:

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);
}

三种保护对比

操作preventExtensionssealfreeze
添加属性禁止禁止禁止
删除属性允许禁止禁止
修改属性值允许允许禁止
修改描述符允许禁止禁止

实际应用

// 不可变配置对象
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
});

参考链接


Share this post on:

Previous Post
JavaScript 正则表达式:语法、断言与常用模式
Next Post
ES6 函数:箭头函数、默认参数与尾调用优化