JavaScript对象与元编程 - JavaScript学习系列文章

发布时间:2026/8/25 11:55:06

JavaScript对象与元编程 - JavaScript学习系列文章
一、属性描述符当你写obj.name张三时, 你真的了解这个name属性吗? 其实每个属性背后都藏着一组属性描述符(Property Descriptor), 就像一个人的身份证信息一样记录着这个属性的详细特征.const obj {name:张三 };const descriptor Object.getOwnPropertyDescriptor(obj,name);console.log(descriptor);/* 输出{value: 张三,writable: true, // 可写enumerable: true, // 可枚举configurable: true // 可配置}*/看到没? 一个简单的属性背后藏着这么多信息! 这些描述符控制着属性的行为:writable:是否可以被修改.enumerable:是否会在for...in循环中出现.configurable:是否可以被删除或修改这些描述符本身.1.1 精细控制属性行为:我们可以用Object.defineProperty()来精细控制属性:const user {};Object.defineProperty(user,secret, {value:123456,writable:false,// 不可写enumerable:false// 不可枚举});console.log(user.secret);// 123456user.secret 654321;// 静默失败严格模式下会报错console.log(user.secret);// 依然是 123456// 这个属性不会出现在 for...in 循环中for (let keyin user) {console.log(key);// 啥也没有}1.2 属性访问器: getter和setter我们可以用 getter 和 setter 创建计算属性:const person {firstName:张,lastName:三,getfullName() {return${this.firstName}${this.lastName};},setfullName(value) {[this.firstName,this.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.firstName,this.lastName] value.split( );},enumerable:true});1.3 批量操作属性描述符: 想一次操作多个属性?Object.defineProperties()了解一下:constconfig {};Object.defineProperties(config, {apiUrl: {value:https://api.example.com,writable:false,enumerable:true},maxRetry: {value:3,writable:true},isDebug: {get() {return localStorage.getItem(debug) true;},set(value) {localStorage.setItem(debug, value);}}});二、SymbolES6 引入了 Symbol 这个新基本类型, 它的主要用途是创建唯一的属性键, 避免命名冲突. 想象一下你给一个第三方库的对象添加属性时, 用字符串作为键名可能会不小心覆盖它原有的属性, Symbol 就解决了这个问题.const id Symbol(这是一个独一无二的标识符);const obj {};obj[id] 秘密数据;// 其他人不知道这个 Symbol就无法访问这个属性console.log(obj[id]);// 秘密数据console.log(Object.keys(obj));// [] - 不会出现在常规遍历中JavaScript内置了一些知名 Symbol, 它们可以改变对象在某些操作下的行为:const myCollection {items: [1,2,3],[Symbol.iterator]:function* () {for (let itemofthis.items) {yield item *2;// 让这个对象可迭代并且每个值都乘以2}}};// 现在可以用 for...of 遍历了for (let xof myCollection) {console.log(x);// 2, 4, 6}其他常用的知名 Symbol:Symbol.toStringTag:自定义对象的toString()行为.Symbol.hasInstance:自定义instanceof的行为.Symbol.toPrimitive:自定义对象转换为原始值的逻辑.Symbol实现私有属性:const _password Symbol(password);classUser {constructor(name, password) {this.name name;this[_password] password;}checkPassword(pwd) {returnthis[_password] pwd;}}const user newUser(张三,123456);console.log(user[_password]);// 理论上可以访问但需要知道这个 Symbolconsole.log(Object.keys(user));// [name] - 不会暴露 Symbol 属性扩展第三方库对象而不污染其属性:// 假设这是一个第三方库的对象const libraryObject {/* ... */ };// 我们想添加自己的数据const myData Symbol(myData);libraryObject[myData] {/* 我们的私有数据 */ };三、Proxy与Reflect3.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) {thrownewTypeError(年龄必须是数字);}target[prop] value;returntrue;// 表示设置成功}};const proxy newProxy(target, handler);console.log(proxy.name);// 打印日志并返回 张三console.log(proxy.xxx);// 打印日志并返回 默认值proxy.age 30;// 正常设置proxy.age 三十;// 抛出 TypeErrorProxy 可以拦截多达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 ReflectReflect 是一个内置对象, 它提供拦截 JavaScript 操作的方法. 这些方法与 Proxy handler 的方法一一对应. Reflect 方法通常用于:1. 更优雅地调用底层操作.2. 与 Proxy 配合使用.const obj {a:1 };// 传统方式console.log(ain obj);// trueconsole.log(delete obj.a);// true// 使用 Reflectconsole.log(Reflect.has(obj,a));// trueconsole.log(Reflect.deleteProperty(obj,a));// true在 Proxy 中使用 Reflect 可以保持默认行为:const proxy newProxy({a:1 }, {get(target, prop, receiver) {console.log(获取属性${prop});// 保持默认的获取行为returnReflect.get(target, prop, receiver);}});四、元编程应用与技巧4.1 可撤销的ProxyProxy 还支持创建可撤销的代理, 之后可以随时取消代理:const target {name:张三 };const { proxy, revoke } Proxy.revocable(target, {get(target, prop) {return拦截的${prop}:${target[prop]};}});console.log(proxy.name);// 拦截的 name: 张三revoke();// 撤销代理console.log(proxy.name);// 抛出 TypeError4.2 多层代理我们可以为同一个对象创建多层代理, 实现不同层次的拦截:const target {};// 第一层代理日志记录const loggingProxy newProxy(target, {get(target, prop) {console.log(读取${prop});return target[prop];},set(target, prop, value) {console.log(设置${prop}${value});target[prop] value;returntrue;}});// 第二层代理验证const validatingProxy newProxy(loggingProxy, {set(target, prop, value) {if (prop age typeof value !number) {thrownewTypeError(年龄必须是数字);}returnReflect.set(target, prop, value);}});validatingProxy.name 张三;// 记录日志并设置validatingProxy.age 30;// 抛出 TypeError4.3 使用Proxy实现观察者模式:functioncreateObservable(target, observer) {returnnewProxy(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:张三,age:30 };const observableUser createObservable(user,(prop, oldVal, newVal) {console.log(属性${prop}从${oldVal}变更为${newVal});});observableUser.name 李四;// 打印变更日志observableUser.age 31;// 打印变更日志4.4 实现一个简易的 ORM 模型:classModel {constructor(data {}) {this._data data;returnthis._createProxy();}_createProxy() {returnnewProxy(this, {get(target, prop) {// 如果是方法调用直接返回if (propin target typeof target[prop] function) {return target[prop].bind(target);}// 否则从 _data 中获取return target._data[prop];},set(target, prop, value) {// 如果是内部属性直接设置if (prop.startsWith(_) || propin target) {target[prop] value;returntrue;}// 否则设置到 _data 中target._data[prop] value;// 模拟触发数据库更新console.log([ORM] 更新字段${prop}${value});returntrue;},has(target, prop) {return propin target._data || propin target;}});}save() {console.log([ORM] 保存数据:,this._data);// 这里应该是实际的保存逻辑}}// 使用示例const user newModel({name:张三,age:30 });console.log(user.name);// 张三user.age 31;// 打印更新日志user.email zhangsanexample.com;// 打印更新日志user.save();// 打印保存日志

相关新闻

JavaScript异步编程的演进 - JavaScript学习系列文章

JavaScript异步编程的演进 - JavaScript学习系列文章

2026/8/25 11:55:06

一、 为什么需要异步编程? 先说说为什么要有异步这回事. JavaScript是单线程的, 也就是说它一次只能做一件事. 如果所有操作都同步执行, 遇到网络请求或者文件读取这种耗时的操作, 页面就会卡住不动, 用户体验直接爆炸. // 同步代码的灾难现场const data fetchDat…

JavaScript 中的原型与继承 - JavaScript学习系列文章

JavaScript 中的原型与继承 - JavaScript学习系列文章

2026/8/25 11:55:06

一、从对象说起, 一切皆对象 在 JS 中, 几乎所有的东西都是对象, 或者说最终都会指向某个对象. 比如: let arr [1, 2, 3];console.log(typeof arr); //"object"function foo() {}console.log(typeof foo); //"function" (但本质上也是对象) 简单的对象创建…

开源考试系统本地部署实战:从环境配置到联调排坑全指南

开源考试系统本地部署实战:从环境配置到联调排坑全指南

2026/8/25 11:55:06

1. 项目缘起:为什么要在本地折腾一个开源考试系统?如果你是一名开发者、教育技术从业者,或者是一个小团队的负责人,想搭建一个在线考试平台,大概率会先想到去网上找现成的开源项目。这个想法很对,毕竟从头造…

Python从零到一:基础语法趣味入门指南

Python从零到一:基础语法趣味入门指南

2026/8/25 12:35:08

在当前 AI 大环境下,Python 语言在编程语言中的比重大幅增长,学习 Python 语言已经成为做 AI 应用开发的重要前置条件。学习 Python 语言就从此刻开始,随我来一起学习了解一下它吧。本文从 Python 语言的基础语法使用开始介绍,逐步…

零基础搭建《我的世界》基岩版私人服务器:从局域网到公网联机全攻略

零基础搭建《我的世界》基岩版私人服务器:从局域网到公网联机全攻略

2026/8/25 12:35:08

想和三五好友一起在《我的世界》基岩版里建造、冒险,却苦于找不到稳定、免费的服务器?或者觉得租用服务器太麻烦、太贵?其实,你完全可以用自己的电脑搭建一个专属的私人服务器!无论是宿舍局域网,还是天南海…

零成本搭建《我的世界》基岩版服务器:从本地部署到外网联机全攻略

零成本搭建《我的世界》基岩版服务器:从本地部署到外网联机全攻略

2026/8/25 12:35:08

这次我们来看一个非常实际的需求:如何在自己的电脑上搭建一个《我的世界》基岩版服务器,实现和朋友联机。很多玩家想和朋友一起玩,但不想租用昂贵的服务器,或者只是想临时开个服体验一下。其实,用你自己的电脑就能搞定…

数据加解密基础知识

数据加解密基础知识

2026/8/25 12:35:08

文章目录数据加解密基础知识笔记为什么要做数据加解密几个必须区分的基础概念加密算法的三大分类对称加密什么是对称加密常见对称加密算法对称加密的优点对称加密的缺点常见使用场景非对称加密什么是非对称加密常见非对称加密算法非对称加密的优点非对称加密的缺点常见使用场景…

仓储货架对楼面载荷怎么算?一个公式搞定(附计算实例)

仓储货架对楼面载荷怎么算?一个公式搞定(附计算实例)

2026/8/25 12:35:08

仓储货架对楼面载荷怎么算?一个公式搞定(附计算实例) 文章目录仓储货架对楼面载荷怎么算?一个公式搞定(附计算实例)前言一、先搞清楚两个概念:平均载荷 vs 集中载荷二、立柱集中载荷计算公式三、…

基于AI大模型的JSON文件高质量汉化实战:告别垃圾机翻

基于AI大模型的JSON文件高质量汉化实战:告别垃圾机翻

2026/8/25 12:25:08

大家好,我是专注于分享实用开发技巧的技术博主。在日常开发或使用各类软件、工具时,我们经常会遇到需要汉化 JSON 配置文件、语言包或插件的情况。传统的汉化工具(如一些自动化脚本或早期工具)往往依赖简单的机器翻译,…

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

2026/8/24 19:53:32

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

2026/8/24 19:56:07

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

2026/8/24 21:16:09

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南

2026/8/25 0:04:34

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory Meta Description:GetQzonehistory 是一个QQ空间历史说…

洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表

洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表

2026/8/25 0:04:35

【题目来源】 https://www.luogu.com.cn/problem/P7912 【题目描述】 小熊的水果店里摆放着一排 n 个水果。每个水果只可能是苹果或桔子,从左到右依次用正整数 1,2,…,n 编号。连续排在一起的同一种水果称为一个“块”。小熊要把这一排水果挑到若干个果篮里&#x…

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG

2026/8/25 0:04:35

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG 【免费下载链接】transformers.js State-of-the-art Machine Learning for the web. Run 🤗 Transformers directly in your browser, with no need for a server! 项目地址: https:/…

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/22 2:02:26

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/22 4:13:47

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/22 1:32:34

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…