惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
F
Fortinet All Blogs
小众软件
小众软件
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Microsoft Azure Blog
Microsoft Azure Blog
J
Java Code Geeks
有赞技术团队
有赞技术团队
D
DataBreaches.Net
Hugging Face - Blog
Hugging Face - Blog
V
Visual Studio Blog
A
About on SuperTechFans
I
InfoQ
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
雷峰网
雷峰网
H
Hackread – Cybersecurity News, Data Breaches, AI and More
罗磊的独立博客
C
Check Point Blog
大猫的无限游戏
大猫的无限游戏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
酷 壳 – CoolShell
酷 壳 – CoolShell
MyScale Blog
MyScale Blog

博客园 - lzhdim

25个每个开发人员都应该掌握的JavaScript 数组方法 20260831 - 个人小作品更新 C#使用SQLite数据库例子 - 开源研究系列文章 20260830 - 个人小作品更新 20260829 - 个人小作品更新 13、JavaScript事件循环机制 - JavaScript学习系列文章 首款媲美分体水冷的一体水冷!微星CORELIQUID E15 360水冷散热器评测:目前为止 锐龙9 9950X3D最强性能释放 JavaScript常见的内存泄露问题 - JavaScript学习系列文章 C#开发的颜色取色器 - 开源研究系列文章 - 个人小作品 成长环境 - 我的闪存 提高 SQL 语句执行速度的方法 JavaScript安全最佳实践 关闭iPhone一些默认设置,让iPhone更流畅 20260814 - 个人小作品更新 20260812 - 个人小作品更新 JavaScript 模块化系统 Markdown 语法完全指南 C# 通过 Windows API 实现进程内存读写操作 JavaScript 函数进阶课题 C#开发的基于钩子技术监测键鼠事件的例子 - 开源项目研究文章 JavaScript异步编程的演进 近十年最帅风冷!酷冷至尊V8 ACE 3DHP散热器评测:单塔能干翻双塔 20260723 - 个人小作品更新 C# 中读取CPU、硬盘和内存温度 雷柏VT3s MAX鼠标评测:中小手型用户也没有被遗忘 顶尖性能的小号电竞鼠标来了 JavaScript 中的原型与继承 1分钟查清局域网所有IP 20260714 - 个人小作品更新 WorkBuddy 完整教程手册 专为AI而生的企业级QLC SSD!长江存储PE501 61.44TB评测:超过1PB的写入测试
JavaScript对象与元编程
lzhdim · 2026-08-13 · via 博客园 - lzhdim

一、属性描述符

当你写 obj.name='张三' 时, 你真的了解这个 name 属性吗? 其实每个属性背后都藏着一组"属性描述符"(Property Descriptor), 就像一个人的身份证信息一样记录着这个属性的详细特征. 

const obj = { name'张三' };const descriptor = Object.getOwnPropertyDescriptor(obj, 'name');console.log(descriptor);/* 输出:{  value: '张三',  writable: true,     // 可写  enumerable: true,   // 可枚举  configurable: true  // 可配置}*/

看到没? 一个简单的属性背后藏着这么多信息! 这些描述符控制着属性的行为:

  1. writable: 是否可以被修改.

  2. enumerable: 是否会在 for...in 循环中出现.

  3. configurable: 是否可以被删除或修改这些描述符本身.

1.1 精细控制属性行为: 

我们可以用 Object.defineProperty() 来精细控制属性: 

const user = {};
Object.defineProperty(user, 'secret', {  value'123456',  writablefalse,      enumerablefalse   });
console.log(user.secret); user.secret = '654321';   console.log(user.secret); 
// 这个属性不会出现在 for...in 循环中for (let key in user) {  console.log(key); }

1.2 属性访问器: getter和setter我们可以用 getter 和 setter 创建"计算属性":

const person = {  firstName'张',  lastName'三',
  get fullName() {    return `${this.firstName} ${this.lastName}`;  },
  set fullName(value) {    [this.firstNamethis.lastName] = value.split(' ');  }};
console.log(person.fullName); person.fullName = '李 四';console.log(person.firstName); 

用 Object.defineProperty 也能实现同样的效果: 

const person = { firstName'张'lastName'三' };
Object.defineProperty(person, 'fullName', {  get() {    return `${this.firstName} ${this.lastName}`;  },  set(value) {    [this.firstNamethis.lastName] = value.split(' ');  },  enumerabletrue});

1.3 批量操作属性描述符: 想一次操作多个属性? Object.defineProperties() 了解一下: 

const config = {};
Object.defineProperties(config, {  apiUrl: {    value'https://api.example.com',    writablefalse,    enumerabletrue  },  maxRetry: {    value3,    writabletrue  },  isDebug: {    get() {      return localStorage.getItem('debug') === 'true';    },    set(value) {      localStorage.setItem('debug', value);    }  }});

二、Symbol

ES6 引入了 Symbol 这个新基本类型, 它的主要用途是创建唯一的属性键, 避免命名冲突. 想象一下你给一个第三方库的对象添加属性时, 用字符串作为键名可能会不小心覆盖它原有的属性, Symbol 就解决了这个问题. 

const id = Symbol('这是一个独一无二的标识符');const obj = {};obj[id] = '秘密数据';
// 其他人不知道这个 Symbol,就无法访问这个属性console.log(obj[id]); console.log(Object.keys(obj)); 

JavaScript内置了一些"知名 Symbol", 它们可以改变对象在某些操作下的行为: 

const myCollection = {  items: [123],  [Symbol.iterator]: function* () {    for (let item of this.items) {      yield item * 2    }  }};
// 现在可以用 for...of 遍历了for (let x of myCollection) {  console.log(x); }

其他常用的知名 Symbol: 

  • Symbol.toStringTag: 自定义对象的 toString() 行为.

  • Symbol.hasInstance: 自定义 instanceof 的行为.

  • Symbol.toPrimitive: 自定义对象转换为原始值的逻辑.

Symbol实现私有属性: 

const _password = Symbol('password');
class User {  constructor(name, password) {    this.name = name;    this[_password] = password;  }
  checkPassword(pwd) {    return this[_password] === pwd;  }}
const user = new User('张三''123456');console.log(user[_password]); console.log(Object.keys(user)); 

扩展第三方库对象而不污染其属性: 

// 假设这是一个第三方库的对象const libraryObject = { /* ... */ };
// 我们想添加自己的数据const myData = Symbol('myData');libraryObject[myData] = { /* 我们的私有数据 */ };

三、Proxy与Reflect

3.1 Proxy基础

Proxy 是 ES6 引入的最强大的元编程特性之一, 它可以创建一个对象的代理, 拦截并自定义对象的基本操作. 

const target = { name'张三' };const handler = {  get(target, prop) {    console.log(`有人想获取属性 ${prop}`);    return target[prop] || '默认值';  },  set(target, prop, value) {    console.log(`有人想设置属性 ${prop} 为 ${value}`);    if (prop === 'age' && typeof value !== 'number') {      throw new TypeError('年龄必须是数字');    }    target[prop] = value;    return true  }};
const proxy = new Proxy(target, handler);
console.log(proxy.name); console.log(proxy.xxx);  proxy.age = 30;          proxy.age = '三十';      

Proxy 可以拦截多达13种操作, 这里列举几个常用的: 

const richHandler = {  // 拦截属性读取  get(target, prop) { 
  // 拦截属性设置  set(target, prop, value) { 
  // 拦截 in 操作符  has(target, prop) { 
  // 拦截 delete 操作  deleteProperty(target, prop) { 
  // 拦截 Object.keys() 等操作  ownKeys(target) { 
  // 拦截函数调用(当目标是函数时)  apply(target, thisArg, args) { 
  // 拦截 new 操作(当目标是构造函数时)  construct(target, args) { 
  // 拦截 Object.getOwnPropertyDescriptor()  getOwnPropertyDescriptor(target, prop) { };

3.2 Reflect

Reflect 是一个内置对象, 它提供拦截 JavaScript 操作的方法. 这些方法与 Proxy handler 的方法一一对应. Reflect 方法通常用于:

1. 更优雅地调用底层操作.

2. 与 Proxy 配合使用.

const obj = { a1 };
// 传统方式console.log('a' in obj);            console.log(delete obj.a);          
// 使用 Reflectconsole.log(Reflect.has(obj, 'a')); console.log(Reflect.deleteProperty(obj, 'a')); 

在 Proxy 中使用 Reflect 可以保持默认行为: 

const proxy = new Proxy({ a1 }, {  get(target, prop, receiver) {    console.log(`获取属性 ${prop}`);    // 保持默认的获取行为    return Reflect.get(target, prop, receiver);  }});

四、元编程应用与技巧

4.1 可撤销的Proxy

Proxy 还支持创建"可撤销"的代理, 之后可以随时取消代理: 

const target = { name'张三' };const { proxy, revoke } = Proxy.revocable(target, {  get(target, prop) {    return `拦截的 ${prop}${target[prop]}`;  }});
console.log(proxy.name); revoke(); // 撤销代理console.log(proxy.name); 

4.2 多层代理

我们可以为同一个对象创建多层代理, 实现不同层次的拦截: 

const target = {};
// 第一层代理:日志记录const loggingProxy = new Proxy(target, {  get(target, prop) {    console.log(`读取 ${prop}`);    return target[prop];  },  set(target, prop, value) {    console.log(`设置 ${prop} = ${value}`);    target[prop] = value;    return true;  }});
// 第二层代理:验证const validatingProxy = new Proxy(loggingProxy, {  set(target, prop, value) {    if (prop === 'age' && typeof value !== 'number') {      throw new TypeError('年龄必须是数字');    }    return Reflect.set(target, prop, value);  }});
validatingProxy.name = '张三'validatingProxy.age = '30';    

4.3 使用Proxy实现观察者模式: 

function createObservable(target, observer) {  return new Proxy(target, {    set(target, prop, value, receiver) {      const oldValue = target[prop];      const result = Reflect.set(target, prop, value, receiver);      if (result && oldValue !== value) {        observer(prop, oldValue, value);      }      return result;    }  });}
const user = { name'张三'age30 };const observableUser = createObservable(user, (prop, oldVal, newVal) => {  console.log(`属性 ${prop} 从 ${oldVal} 变更为 ${newVal}`);});
observableUser.name = '李四'observableUser.age = 31;     

4.4 实现一个简易的 ORM 模型: 

class Model {  constructor(data = {}) {    this._data = data;    return this._createProxy();  }
  _createProxy() {    return new Proxy(this, {      get(target, prop) {        // 如果是方法调用,直接返回        if (prop in target && typeof target[prop] === 'function') {          return target[prop].bind(target);        }
        // 否则从 _data 中获取        return target._data[prop];      },
      set(target, prop, value) {        // 如果是内部属性,直接设置        if (prop.startsWith('_') || prop in target) {          target[prop] = value;          return true;        }
        // 否则设置到 _data 中        target._data[prop] = value;
        // 模拟触发数据库更新        console.log(`[ORM] 更新字段 ${prop} = ${value}`);        return true;      },
      has(target, prop) {        return prop in target._data || prop in target;      }    });  }
  save() {    console.log('[ORM] 保存数据:'this._data);    // 这里应该是实际的保存逻辑  }}
// 使用示例const user = new Model({ name'张三'age30 });console.log(user.name); user.age = 31;          user.email = 'zhangsan@example.com'user.save();            // 打印保存日志